From f96a27fe8a9f77a9c72dc87846d23fd6e8218713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 11:46:02 +0100 Subject: [PATCH 01/14] BackgroundTasks: Move setup and SOAP classes into the component Move the setup agent, its config and objectives, and the SOAP administration class into components/ILIAS/BackgroundTasks/src under the ILIAS\BackgroundTasks namespace. Delete the old files from BackgroundTasks_ and the empty class.ilBackgroundTasksDB80.php. ilBackgroundTasksDBHotfixes10 keeps its global class name, because the database step log stores executed steps by that class name. --- .../ILIAS/BackgroundTasks/BackgroundTasks.php | 4 ++-- .../src/SOAP/Administration.php} | 8 ++++---- .../Setup/Agent.php} | 19 +++++++++++-------- .../src}/Setup/BackgoundWorkerObjective.php | 3 +-- .../src/Setup/ConfigStoredObjective.php} | 9 ++++++--- .../src/Setup/MetricsCollectedObjective.php} | 5 ++++- .../src/Setup/SetupConfig.php} | 4 +++- .../Setup/class.ilBackgroundTasksDB80.php | 0 .../ILIAS/soap/include/inc.soap_functions.php | 2 +- 9 files changed, 32 insertions(+), 22 deletions(-) rename components/ILIAS/{BackgroundTasks_/classes/class.ilSoapBackgroundTasksAdministration.php => BackgroundTasks/src/SOAP/Administration.php} (85%) rename components/ILIAS/BackgroundTasks/{classes/Setup/class.ilBackgroundTasksSetupAgent.php => src/Setup/Agent.php} (84%) rename components/ILIAS/{BackgroundTasks_/classes => BackgroundTasks/src}/Setup/BackgoundWorkerObjective.php (98%) rename components/ILIAS/{BackgroundTasks_/classes/Setup/class.ilBackgroundTasksConfigStoredObjective.php => BackgroundTasks/src/Setup/ConfigStoredObjective.php} (90%) rename components/ILIAS/{BackgroundTasks_/classes/Setup/class.ilBackgroundTasksMetricsCollectedObjective.php => BackgroundTasks/src/Setup/MetricsCollectedObjective.php} (92%) rename components/ILIAS/{BackgroundTasks_/classes/Setup/class.ilBackgroundTasksSetupConfig.php => BackgroundTasks/src/Setup/SetupConfig.php} (95%) delete mode 100755 components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksDB80.php diff --git a/components/ILIAS/BackgroundTasks/BackgroundTasks.php b/components/ILIAS/BackgroundTasks/BackgroundTasks.php index 499dcbc2228e..5e4336add297 100644 --- a/components/ILIAS/BackgroundTasks/BackgroundTasks.php +++ b/components/ILIAS/BackgroundTasks/BackgroundTasks.php @@ -38,8 +38,8 @@ public function init( array | \ArrayAccess &$pull, array | \ArrayAccess &$internal, ): void { - $contribute[Agent::class] = static fn(): \ilBackgroundTasksSetupAgent => - new \ilBackgroundTasksSetupAgent( + $contribute[Agent::class] = static fn(): Agent => + new \ILIAS\BackgroundTasks\Setup\Agent( $pull[Factory::class] ); $contribute[LanguageFileDirectory::class] = fn() => new ComponentLanguageFileDirectory( diff --git a/components/ILIAS/BackgroundTasks_/classes/class.ilSoapBackgroundTasksAdministration.php b/components/ILIAS/BackgroundTasks/src/SOAP/Administration.php similarity index 85% rename from components/ILIAS/BackgroundTasks_/classes/class.ilSoapBackgroundTasksAdministration.php rename to components/ILIAS/BackgroundTasks/src/SOAP/Administration.php index 93202a85b98d..b5add484c37a 100755 --- a/components/ILIAS/BackgroundTasks_/classes/class.ilSoapBackgroundTasksAdministration.php +++ b/components/ILIAS/BackgroundTasks/src/SOAP/Administration.php @@ -16,12 +16,12 @@ * *********************************************************************/ -/** @noRector */ -require_once __DIR__ . "/../../../../vendor/composer/vendor/autoload.php"; +namespace ILIAS\BackgroundTasks\SOAP; use ILIAS\BackgroundTasks\Implementation\TaskManager\AsyncTaskManager; +use ilSoapAdministration; -class ilSoapBackgroundTasksAdministration extends ilSoapAdministration +class Administration extends ilSoapAdministration { public function __construct(bool $use_nusoap = true) { @@ -29,7 +29,7 @@ public function __construct(bool $use_nusoap = true) } /** - * @return bool|soap_fault|SoapFault|null + * @return bool|\soap_fault|\SoapFault|null */ public function runAsync(string $sid) { diff --git a/components/ILIAS/BackgroundTasks/classes/Setup/class.ilBackgroundTasksSetupAgent.php b/components/ILIAS/BackgroundTasks/src/Setup/Agent.php similarity index 84% rename from components/ILIAS/BackgroundTasks/classes/Setup/class.ilBackgroundTasksSetupAgent.php rename to components/ILIAS/BackgroundTasks/src/Setup/Agent.php index bade8b809230..f7fd7af9b9fd 100755 --- a/components/ILIAS/BackgroundTasks/classes/Setup/class.ilBackgroundTasksSetupAgent.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/Agent.php @@ -16,7 +16,8 @@ * *********************************************************************/ -use ILIAS\Setup\Agent; +namespace ILIAS\BackgroundTasks\Setup; + use ILIAS\Setup\Agent\HasNoNamedObjective; use ILIAS\Refinery\Factory; use ILIAS\Refinery\Transformation; @@ -25,10 +26,12 @@ use ILIAS\Setup\Objective\NullObjective; use ILIAS\Setup\Metrics\Storage; use ILIAS\Setup\ObjectiveCollection; -use ILIAS\BackgroundTasks\Setup\BackgoundWorkerObjective; use ILIAS\Setup\ObjectiveConstructor; +use ilBackgroundTasksDBHotfixes10; +use ilDatabaseUpdateStepsExecutedObjective; +use ilDatabaseUpdateStepsMetricsCollectedObjective; -class ilBackgroundTasksSetupAgent implements Agent +class Agent implements \ILIAS\Setup\Agent { use HasNoNamedObjective; @@ -49,8 +52,8 @@ public function hasConfig(): bool */ public function getArrayToConfigTransformation(): Transformation { - return $this->refinery->custom()->transformation(fn($data): \ilBackgroundTasksSetupConfig => new \ilBackgroundTasksSetupConfig( - $data["type"] ?? \ilBackgroundTasksSetupConfig::TYPE_SYNCHRONOUS, + return $this->refinery->custom()->transformation(fn($data): SetupConfig => new SetupConfig( + $data["type"] ?? SetupConfig::TYPE_SYNCHRONOUS, $data["max_number_of_concurrent_tasks"] ?? 1 )); } @@ -68,14 +71,14 @@ public function getInstallObjective(?Config $config = null): Objective */ public function getUpdateObjective(?Config $config = null): Objective { - if (!$config instanceof ilBackgroundTasksSetupConfig) { + if (!$config instanceof SetupConfig) { return new NullObjective(); } return new ObjectiveCollection( 'BackgroundTasks', true, - new ilBackgroundTasksConfigStoredObjective($config), + new ConfigStoredObjective($config), new ilDatabaseUpdateStepsExecutedObjective(new ilBackgroundTasksDBHotfixes10()) ); } @@ -96,7 +99,7 @@ public function getStatusObjective(Storage $storage): Objective return new ObjectiveCollection( 'Component BackgroundTasks', true, - new ilBackgroundTasksMetricsCollectedObjective($storage), + new MetricsCollectedObjective($storage), new ilDatabaseUpdateStepsMetricsCollectedObjective($storage, new ilBackgroundTasksDBHotfixes10()) ); } diff --git a/components/ILIAS/BackgroundTasks_/classes/Setup/BackgoundWorkerObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php similarity index 98% rename from components/ILIAS/BackgroundTasks_/classes/Setup/BackgoundWorkerObjective.php rename to components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php index 7fb67bc56d18..f4b26b4164e6 100644 --- a/components/ILIAS/BackgroundTasks_/classes/Setup/BackgoundWorkerObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php @@ -37,7 +37,6 @@ use ilSettingsFactoryExistsObjective; use ILIAS\Setup\Objective\ClientIdReadObjective; use ILIAS\Setup\Objective\AdminConfirmedObjective; -use ilBackgroundTasksSetupConfig; class BackgoundWorkerObjective extends AdminConfirmedObjective { @@ -92,7 +91,7 @@ public function isApplicable(Environment $environment): bool return false; } - return $ini->readVariable('background_tasks', 'concurrency') === ilBackgroundTasksSetupConfig::TYPE_ASYNCHRONOUS; + return $ini->readVariable('background_tasks', 'concurrency') === SetupConfig::TYPE_ASYNCHRONOUS; } public function achieve(Environment $environment): Environment diff --git a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksConfigStoredObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php similarity index 90% rename from components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksConfigStoredObjective.php rename to components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php index 7bb8908b32a1..fca94fc024b8 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksConfigStoredObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php @@ -16,13 +16,16 @@ * *********************************************************************/ +namespace ILIAS\BackgroundTasks\Setup; + use ILIAS\Setup\Objective; use ILIAS\Setup\Environment; use ILIAS\Setup\UnachievableException; +use ilIniFilesLoadedObjective; -class ilBackgroundTasksConfigStoredObjective implements Objective +class ConfigStoredObjective implements Objective { - public function __construct(protected ilBackgroundTasksSetupConfig $config) + public function __construct(protected SetupConfig $config) { } @@ -59,7 +62,7 @@ public function achieve(Environment $environment): Environment $ini->addGroup("background_tasks"); } - if ($this->config->getType() === \ilBackgroundTasksSetupConfig::TYPE_ASYNCHRONOUS) { + if ($this->config->getType() === SetupConfig::TYPE_ASYNCHRONOUS) { $io = $environment->getResource(Environment::RESOURCE_ADMIN_INTERACTION); /** @var Setup\CLI\IOWrapper $io */ $io->inform('Asynchronous background tasks need SOAP administration to be enabled. Make sure to enable it in your configuarion.'); diff --git a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksMetricsCollectedObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php similarity index 92% rename from components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksMetricsCollectedObjective.php rename to components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php index 0236691d7251..08b7490069c3 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksMetricsCollectedObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php @@ -16,11 +16,14 @@ * *********************************************************************/ +namespace ILIAS\BackgroundTasks\Setup; + use ILIAS\Setup\Metrics\CollectedObjective; use ILIAS\Setup\Environment; use ILIAS\Setup\Metrics\Storage; +use ilIniFilesLoadedObjective; -class ilBackgroundTasksMetricsCollectedObjective extends CollectedObjective +class MetricsCollectedObjective extends CollectedObjective { /** * @return \ilIniFilesLoadedObjective[] diff --git a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksSetupConfig.php b/components/ILIAS/BackgroundTasks/src/Setup/SetupConfig.php similarity index 95% rename from components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksSetupConfig.php rename to components/ILIAS/BackgroundTasks/src/Setup/SetupConfig.php index 4f2c7003399c..068042a8a526 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksSetupConfig.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/SetupConfig.php @@ -16,9 +16,11 @@ * *********************************************************************/ +namespace ILIAS\BackgroundTasks\Setup; + use ILIAS\Setup\Config; -class ilBackgroundTasksSetupConfig implements Config +class SetupConfig implements Config { public const TYPE_SYNCHRONOUS = "sync"; public const TYPE_ASYNCHRONOUS = "async"; diff --git a/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksDB80.php b/components/ILIAS/BackgroundTasks_/classes/Setup/class.ilBackgroundTasksDB80.php deleted file mode 100755 index e69de29bb2d1..000000000000 diff --git a/components/ILIAS/soap/include/inc.soap_functions.php b/components/ILIAS/soap/include/inc.soap_functions.php index 1eb6de6cba15..c1995cfe6aef 100755 --- a/components/ILIAS/soap/include/inc.soap_functions.php +++ b/components/ILIAS/soap/include/inc.soap_functions.php @@ -676,7 +676,7 @@ public static function copyObject(string $sid, string $copy_settings_xml) */ public static function startBackgroundTaskWorker(string $sid) { - $soa = new ilSoapBackgroundTasksAdministration(); + $soa = new \ILIAS\BackgroundTasks\SOAP\Administration(); return $soa->runAsync($sid); } From 9a9f914784af25ce38791a51d726e421578bff4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 11:52:18 +0100 Subject: [PATCH 02/14] BackgroundTasks: Remove redundant docblocks from moved setup classes Delete @inheritdoc blocks and @return or @throws tags that restate the interface or the method body. Delete the @var tag for $io in ConfigStoredObjective, which named a class that does not exist. Drop the two imports that only these docblocks used. --- .../ILIAS/BackgroundTasks/src/Setup/Agent.php | 21 ------------------- .../src/Setup/BackgoundWorkerObjective.php | 11 ---------- .../src/Setup/ConfigStoredObjective.php | 7 ------- .../src/Setup/MetricsCollectedObjective.php | 3 --- 4 files changed, 42 deletions(-) diff --git a/components/ILIAS/BackgroundTasks/src/Setup/Agent.php b/components/ILIAS/BackgroundTasks/src/Setup/Agent.php index f7fd7af9b9fd..15d7571626ea 100755 --- a/components/ILIAS/BackgroundTasks/src/Setup/Agent.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/Agent.php @@ -39,17 +39,11 @@ public function __construct(protected Factory $refinery) { } - /** - * @inheritdoc - */ public function hasConfig(): bool { return true; } - /** - * @inheritdoc - */ public function getArrayToConfigTransformation(): Transformation { return $this->refinery->custom()->transformation(fn($data): SetupConfig => new SetupConfig( @@ -58,17 +52,11 @@ public function getArrayToConfigTransformation(): Transformation )); } - /** - * @inheritdoc - */ public function getInstallObjective(?Config $config = null): Objective { return $this->getUpdateObjective($config); } - /** - * @inheritdoc - */ public function getUpdateObjective(?Config $config = null): Objective { if (!$config instanceof SetupConfig) { @@ -83,17 +71,11 @@ public function getUpdateObjective(?Config $config = null): Objective ); } - /** - * @inheritdoc - */ public function getBuildObjective(): Objective { return new NullObjective(); } - /** - * @inheritdoc - */ public function getStatusObjective(Storage $storage): Objective { return new ObjectiveCollection( @@ -114,9 +96,6 @@ public function getNamedObjectives(?Config $config = null): array ]; } - /** - * @inheritDoc - */ public function getMigrations(): array { return []; diff --git a/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php index f4b26b4164e6..dfeaa47fbc68 100644 --- a/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/BackgoundWorkerObjective.php @@ -28,9 +28,7 @@ use ilCurlConnection; use RuntimeException; use ILIAS\DI\Container; -use ILIAS\Setup\Objective; use ILIAS\Setup\Environment; -use ilCurlConnectionException; use ILIAS\Setup\CLI\IOWrapper; use ilIniFilesPopulatedObjective; use ilDatabaseInitializedObjective; @@ -67,9 +65,6 @@ public function isNotable(): bool return true; } - /** - * @return Objective[] - */ public function getPreconditions(Environment $environment): array { return [ @@ -80,9 +75,6 @@ public function getPreconditions(Environment $environment): array ]; } - /** - * @inheritDoc - */ public function isApplicable(Environment $environment): bool { $ini = $environment->getResource(Environment::RESOURCE_ILIAS_INI); @@ -193,9 +185,6 @@ private function error(string $text): void $this->io->error($text); } - /** - * @throws ilCurlConnectionException - */ private function getCurlConnection(ilSetting $settings, string $url): ilCurlConnection { $curl = new ilCurlConnection( diff --git a/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php index fca94fc024b8..2011b7a29590 100755 --- a/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php @@ -44,9 +44,6 @@ public function isNotable(): bool return false; } - /** - * @return \ilIniFilesLoadedObjective[] - */ public function getPreconditions(Environment $environment): array { return [ @@ -64,7 +61,6 @@ public function achieve(Environment $environment): Environment if ($this->config->getType() === SetupConfig::TYPE_ASYNCHRONOUS) { $io = $environment->getResource(Environment::RESOURCE_ADMIN_INTERACTION); - /** @var Setup\CLI\IOWrapper $io */ $io->inform('Asynchronous background tasks need SOAP administration to be enabled. Make sure to enable it in your configuarion.'); } @@ -78,9 +74,6 @@ public function achieve(Environment $environment): Environment return $environment; } - /** - * @inheritDoc - */ public function isApplicable(Environment $environment): bool { $ini = $environment->getResource(Environment::RESOURCE_ILIAS_INI); diff --git a/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php index 08b7490069c3..c03c5a82fd97 100755 --- a/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/MetricsCollectedObjective.php @@ -25,9 +25,6 @@ class MetricsCollectedObjective extends CollectedObjective { - /** - * @return \ilIniFilesLoadedObjective[] - */ public function getTentativePreconditions(Environment $environment): array { return [ From e4becf21ce3ba2a9bfec275c776d1e827ff2669d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 12:25:08 +0100 Subject: [PATCH 03/14] BackgroundTasks: Move notification UI into the component Move ilBTControllerGUI and ilBTPopOverGUI into components/ILIAS/BackgroundTasks/src/Implementation/UI as ControllerGUI and PopOverGUI, next to the StateTranslator trait, which keeps its namespace. BTNotificationProvider moves to src/Provider and keeps its class name, so its GlobalScreen provider id does not change. Move background_task_refresh.js and its asset contribution from BackgroundTasks_ to BackgroundTasks. The public path stays assets/js/background_task_refresh.js. Move service.xml with the component id bgtk, point its baseclass entry at ControllerGUI, and drop the gsprovider entry for ilBTGSProvider, a class that does not exist. BackgroundTasks_ keeps the download jobs and an empty component class, because cli/build_bootstrap.php expects one in every component directory. --- .../ILIAS/BackgroundTasks/BackgroundTasks.php | 7 ++++ .../resources/background_task_refresh.js | 0 components/ILIAS/BackgroundTasks/service.xml | 2 +- .../src/Implementation/UI/ControllerGUI.php} | 11 +++-- .../src/Implementation/UI/PopOverGUI.php} | 41 +++++++++---------- .../Implementation/UI}/StateTranslator.php | 0 .../src}/Provider/BTNotificationProvider.php | 4 +- .../BackgroundTasks_/BackgroundTasks_.php | 4 -- 8 files changed, 37 insertions(+), 32 deletions(-) rename components/ILIAS/{BackgroundTasks_ => BackgroundTasks}/resources/background_task_refresh.js (100%) rename components/ILIAS/{BackgroundTasks_/classes/class.ilBTControllerGUI.php => BackgroundTasks/src/Implementation/UI/ControllerGUI.php} (97%) rename components/ILIAS/{BackgroundTasks_/classes/class.ilBTPopOverGUI.php => BackgroundTasks/src/Implementation/UI/PopOverGUI.php} (83%) rename components/ILIAS/{BackgroundTasks_/classes => BackgroundTasks/src/Implementation/UI}/StateTranslator.php (100%) rename components/ILIAS/{BackgroundTasks_/classes => BackgroundTasks/src}/Provider/BTNotificationProvider.php (95%) diff --git a/components/ILIAS/BackgroundTasks/BackgroundTasks.php b/components/ILIAS/BackgroundTasks/BackgroundTasks.php index 5e4336add297..418937558208 100644 --- a/components/ILIAS/BackgroundTasks/BackgroundTasks.php +++ b/components/ILIAS/BackgroundTasks/BackgroundTasks.php @@ -21,6 +21,8 @@ namespace ILIAS; use ILIAS\Component\Component; +use ILIAS\Component\Resource\PublicAsset; +use ILIAS\Component\Resource\ComponentJS; use ILIAS\Setup\Agent; use ILIAS\Refinery\Factory; use ILIAS\Language\ComponentTranslation\LanguageFileDirectory; @@ -50,5 +52,10 @@ public function init( $this, 'background_tasks' ); + + $contribute[PublicAsset::class] = fn(): ComponentJS => new ComponentJS( + $this, + "background_task_refresh.js" + ); } } diff --git a/components/ILIAS/BackgroundTasks_/resources/background_task_refresh.js b/components/ILIAS/BackgroundTasks/resources/background_task_refresh.js similarity index 100% rename from components/ILIAS/BackgroundTasks_/resources/background_task_refresh.js rename to components/ILIAS/BackgroundTasks/resources/background_task_refresh.js diff --git a/components/ILIAS/BackgroundTasks/service.xml b/components/ILIAS/BackgroundTasks/service.xml index 7b1d010dddd7..611bb9016ad9 100644 --- a/components/ILIAS/BackgroundTasks/service.xml +++ b/components/ILIAS/BackgroundTasks/service.xml @@ -2,7 +2,7 @@ - + diff --git a/components/ILIAS/BackgroundTasks_/classes/class.ilBTControllerGUI.php b/components/ILIAS/BackgroundTasks/src/Implementation/UI/ControllerGUI.php similarity index 97% rename from components/ILIAS/BackgroundTasks_/classes/class.ilBTControllerGUI.php rename to components/ILIAS/BackgroundTasks/src/Implementation/UI/ControllerGUI.php index f6e9d13d048c..e4b70def358b 100755 --- a/components/ILIAS/BackgroundTasks_/classes/class.ilBTControllerGUI.php +++ b/components/ILIAS/BackgroundTasks/src/Implementation/UI/ControllerGUI.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\BackgroundTasks\Implementation\UI; + use ILIAS\HTTP\StatusCode; use ILIAS\Filesystem\Stream\Streams; use ILIAS\Data\URI; @@ -25,14 +27,15 @@ use ILIAS\HTTP\Response\ResponseHeader; use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionOption; use ILIAS\components\OrgUnit\ARHelper\DIC; +use ilCtrlBaseClassInterface; +use ilCtrlSecurityInterface; +use ilUtil; /** - * Class ilBTControllerGUI - * * @author Oskar Truffer * @author Fabian Schmid */ -class ilBTControllerGUI implements ilCtrlBaseClassInterface, ilCtrlSecurityInterface +class ControllerGUI implements ilCtrlBaseClassInterface, ilCtrlSecurityInterface { use DIC; public const FROM_URL = 'from_url'; @@ -130,7 +133,7 @@ protected function getAsyncReplacementItem(): void $this->enforceBucketBelongsToCurrentUser($bucket); - $item_source = new ilBTPopOverGUI($this->dic()); + $item_source = new PopOverGUI($this->dic()); $this->dic()->language()->loadLanguageModule('background_tasks'); $item = $item_source->getItemForObserver($bucket); diff --git a/components/ILIAS/BackgroundTasks_/classes/class.ilBTPopOverGUI.php b/components/ILIAS/BackgroundTasks/src/Implementation/UI/PopOverGUI.php similarity index 83% rename from components/ILIAS/BackgroundTasks_/classes/class.ilBTPopOverGUI.php rename to components/ILIAS/BackgroundTasks/src/Implementation/UI/PopOverGUI.php index 8577b6f034b3..0fe443677731 100755 --- a/components/ILIAS/BackgroundTasks_/classes/class.ilBTPopOverGUI.php +++ b/components/ILIAS/BackgroundTasks/src/Implementation/UI/PopOverGUI.php @@ -16,13 +16,14 @@ * *********************************************************************/ +namespace ILIAS\BackgroundTasks\Implementation\UI; + use ILIAS\DI\Container; use ILIAS\UI\Component\Item\Notification; use ILIAS\BackgroundTasks\Task\UserInteraction\Option; use ILIAS\BackgroundTasks\Bucket; use ILIAS\BackgroundTasks\Implementation\Bucket\State; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractTask; -use ILIAS\BackgroundTasks\Implementation\UI\StateTranslator; use ILIAS\BackgroundTasks\Task\UserInteraction; use ILIAS\UI\Component\Button\Button; use ILIAS\UI\Component\Button\Shy; @@ -30,12 +31,10 @@ use ILIAS\BackgroundTasks\Task\Job; /** - * Class ilBTPopOverGUI - * * @author Oskar Truffer * @author Fabian Schmid */ -class ilBTPopOverGUI +class PopOverGUI { use StateTranslator; @@ -168,18 +167,18 @@ public function getUserInteractionContent(Bucket $observer, string $redirect_uri return array_map( function (Option $option) use ($ctrl, $factory, $observer, $persistence, $redirect_uri, $language): Shy { $ctrl->setParameterByClass( - ilBTControllerGUI::class, - ilBTControllerGUI::FROM_URL, - ilBTControllerGUI::hash("//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}") + ControllerGUI::class, + ControllerGUI::FROM_URL, + ControllerGUI::hash("//{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}") ); $ctrl->setParameterByClass( - ilBTControllerGUI::class, - ilBTControllerGUI::SELECTED_OPTION, + ControllerGUI::class, + ControllerGUI::SELECTED_OPTION, $option->getValue() ); $ctrl->setParameterByClass( - ilBTControllerGUI::class, - ilBTControllerGUI::OBSERVER_ID, + ControllerGUI::class, + ControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer) ); $this->addFromUrlToNextRequest($redirect_uri); @@ -187,7 +186,7 @@ function (Option $option) use ($ctrl, $factory, $observer, $persistence, $redire return $factory->button() ->shy( $language->txt($option->getLangVar()), - $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_USER_INTERACTION) + $ctrl->getLinkTargetByClass([ControllerGUI::class], ControllerGUI::CMD_USER_INTERACTION) ); }, $options @@ -227,20 +226,20 @@ private function getCloseButtonAction(Option $option, string $redirect_uri, Buck { $ctrl = $this->dic->ctrl(); $persistence = $this->dic->backgroundTasks()->persistence(); - $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer)); + $ctrl->setParameterByClass(ControllerGUI::class, ControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer)); $this->addFromUrlToNextRequest($redirect_uri); - $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::IS_ASYNC, "true"); + $ctrl->setParameterByClass(ControllerGUI::class, ControllerGUI::IS_ASYNC, "true"); switch ($option->getValue()) { case AbstractTask::MAIN_ABORT: - $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_ABORT); + $action = $ctrl->getLinkTargetByClass([ControllerGUI::class], ControllerGUI::CMD_ABORT); break; case AbstractTask::MAIN_REMOVE: - $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_REMOVE); + $action = $ctrl->getLinkTargetByClass([ControllerGUI::class], ControllerGUI::CMD_REMOVE); break; default: - $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::SELECTED_OPTION, $option->getValue()); - $action = $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_USER_INTERACTION); + $ctrl->setParameterByClass(ControllerGUI::class, ControllerGUI::SELECTED_OPTION, $option->getValue()); + $action = $ctrl->getLinkTargetByClass([ControllerGUI::class], ControllerGUI::CMD_USER_INTERACTION); break; } @@ -252,15 +251,15 @@ private function getRefreshUrl(Bucket $observer): string { $ctrl = $this->dic->ctrl(); $persistence = $this->dic->backgroundTasks()->persistence(); - $ctrl->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer)); + $ctrl->setParameterByClass(ControllerGUI::class, ControllerGUI::OBSERVER_ID, $persistence->getBucketContainerId($observer)); - return $ctrl->getLinkTargetByClass([ilBTControllerGUI::class], ilBTControllerGUI::CMD_GET_REPLACEMENT_ITEM); + return $ctrl->getLinkTargetByClass([ControllerGUI::class], ControllerGUI::CMD_GET_REPLACEMENT_ITEM); } private function addFromUrlToNextRequest(string $redirect_uri): void { - $this->dic->ctrl()->setParameterByClass(ilBTControllerGUI::class, ilBTControllerGUI::FROM_URL, ilBTControllerGUI::hash($redirect_uri)); + $this->dic->ctrl()->setParameterByClass(ControllerGUI::class, ControllerGUI::FROM_URL, ControllerGUI::hash($redirect_uri)); } diff --git a/components/ILIAS/BackgroundTasks_/classes/StateTranslator.php b/components/ILIAS/BackgroundTasks/src/Implementation/UI/StateTranslator.php similarity index 100% rename from components/ILIAS/BackgroundTasks_/classes/StateTranslator.php rename to components/ILIAS/BackgroundTasks/src/Implementation/UI/StateTranslator.php diff --git a/components/ILIAS/BackgroundTasks_/classes/Provider/BTNotificationProvider.php b/components/ILIAS/BackgroundTasks/src/Provider/BTNotificationProvider.php similarity index 95% rename from components/ILIAS/BackgroundTasks_/classes/Provider/BTNotificationProvider.php rename to components/ILIAS/BackgroundTasks/src/Provider/BTNotificationProvider.php index 75f9124674a1..bd50973a09ba 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Provider/BTNotificationProvider.php +++ b/components/ILIAS/BackgroundTasks/src/Provider/BTNotificationProvider.php @@ -18,7 +18,7 @@ namespace ILIAS\BackgroundTasks\Provider; -use ilBTPopOverGUI; +use ILIAS\BackgroundTasks\Implementation\UI\PopOverGUI; use ILIAS\GlobalScreen\Identification\IdentificationInterface; use ILIAS\GlobalScreen\Scope\Notification\Provider\AbstractNotificationProvider; use ILIAS\GlobalScreen\Scope\Notification\Provider\NotificationProvider; @@ -47,7 +47,7 @@ public function getNotifications(): array $factory = $this->globalScreen()->notifications()->factory(); - $item_source = new ilBTPopOverGUI($this->dic); + $item_source = new PopOverGUI($this->dic); $group = $factory->standardGroup($id('bg_task_bucket_group')) ->withTitle($this->txt('background_tasks')) ->addNotification( diff --git a/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php b/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php index 150aa951659a..2e4e707e4b00 100644 --- a/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php +++ b/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php @@ -21,8 +21,6 @@ namespace ILIAS; use ILIAS\Component\Component; -use ILIAS\Component\Resource\PublicAsset; -use ILIAS\Component\Resource\ComponentJS; class BackgroundTasks_ implements Component { @@ -36,7 +34,5 @@ public function init( array | \ArrayAccess &$pull, array | \ArrayAccess &$internal, ): void { - $contribute[PublicAsset::class] = fn(): ComponentJS => - new ComponentJS($this, "background_task_refresh.js"); } } From 81d9092d166af54e6d75141fb186dd7d90a49231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 11:47:23 +0100 Subject: [PATCH 04/14] FileServices: Move shared download jobs out of BackgroundTasks_ The ZIP download jobs used by Container and WorkspaceFolder now live in FileServices under the namespace ILIAS\FileServices\Download. The old files in BackgroundTasks_ are deleted. No class alias remains. Background task buckets store the task class name in the database. A pending ZIP download that was created before the update cannot load after it and BackgroundTasks deletes that bucket. The user starts the download again. --- ...ilDownloadContainerFilesBackgroundTask.php | 21 ++++++++++----- .../src/Download/CheckSumOfFileSizesJob.php} | 18 +++++++------ .../src/Download/CollectFilesJob.php} | 26 ++++++++++--------- .../src/Download/CopyDefinition.php} | 4 ++- .../Download/CopyFilesToTempDirectoryJob.php} | 22 +++++++++------- .../src/Download/DownloadZipInteraction.php} | 10 ++++--- .../SumOfFileSizesTooLargeInteraction.php} | 11 ++++---- .../src/Download/ZipJob.php} | 8 +++--- ...lDownloadWorkspaceFolderBackgroundTask.php | 6 +++-- 9 files changed, 74 insertions(+), 52 deletions(-) rename components/ILIAS/{BackgroundTasks_/classes/Jobs/class.ilCheckSumOfFileSizesJob.php => FileServices/src/Download/CheckSumOfFileSizesJob.php} (90%) rename components/ILIAS/{BackgroundTasks_/classes/Jobs/class.ilCollectFilesJob.php => FileServices/src/Download/CollectFilesJob.php} (92%) rename components/ILIAS/{BackgroundTasks_/classes/class.ilCopyDefinition.php => FileServices/src/Download/CopyDefinition.php} (98%) rename components/ILIAS/{BackgroundTasks_/classes/Jobs/class.ilCopyFilesToTempDirectoryJob.php => FileServices/src/Download/CopyFilesToTempDirectoryJob.php} (88%) rename components/ILIAS/{BackgroundTasks_/classes/UserInteractions/class.ilDownloadZipInteraction.php => FileServices/src/Download/DownloadZipInteraction.php} (92%) rename components/ILIAS/{BackgroundTasks_/classes/UserInteractions/class.ilSumOfFileSizesTooLargeInteraction.php => FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php} (91%) rename components/ILIAS/{BackgroundTasks_/classes/Jobs/class.ilZipJob.php => FileServices/src/Download/ZipJob.php} (94%) diff --git a/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php b/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php index 0196147d9084..f0cc69868b57 100755 --- a/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php +++ b/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php @@ -19,6 +19,13 @@ use ILIAS\BackgroundTasks\Implementation\Bucket\BasicBucket; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\StringValue; use ILIAS\BackgroundTasks\Task\TaskFactory; +use ILIAS\FileServices\Download\CheckSumOfFileSizesJob; +use ILIAS\FileServices\Download\CollectFilesJob; +use ILIAS\FileServices\Download\CopyDefinition; +use ILIAS\FileServices\Download\CopyFilesToTempDirectoryJob; +use ILIAS\FileServices\Download\DownloadZipInteraction; +use ILIAS\FileServices\Download\SumOfFileSizesTooLargeInteraction; +use ILIAS\FileServices\Download\ZipJob; /** * Class ilDownloadContainerFilesBackgroundTask @@ -83,35 +90,35 @@ public function run(): bool $this->logger->debug('Created bucket and set the following user id: ' . $this->user_id); // Copy Definition - $definition = new ilCopyDefinition(); + $definition = new CopyDefinition(); $normalized_name = ilFileUtils::getASCIIFilename($this->getBucketTitle()); $definition->setTempDir($normalized_name); $definition->setObjectRefIds($this->object_ref_ids); $this->logger->debug('Created copy definition and added the following tempdir: ' . $normalized_name); // Collect all files by the definition and prevent duplicates - $collect_job = $this->task_factory->createTask(ilCollectFilesJob::class, [$definition, $this->initiated_by_folder_action]); + $collect_job = $this->task_factory->createTask(CollectFilesJob::class, [$definition, $this->initiated_by_folder_action]); $this->logger->debug('Collected files based on the following object ids: '); $this->logger->dump($this->object_ref_ids); // Check the FileSize - $file_size_job = $this->task_factory->createTask(ilCheckSumOfFileSizesJob::class, [$collect_job]); + $file_size_job = $this->task_factory->createTask(CheckSumOfFileSizesJob::class, [$collect_job]); // Show problems with file-limit - $file_size_interaction = $this->task_factory->createTask(ilSumOfFileSizesTooLargeInteraction::class, [$file_size_job]); + $file_size_interaction = $this->task_factory->createTask(SumOfFileSizesTooLargeInteraction::class, [$file_size_job]); $this->logger->debug('Determined the sum of all file sizes'); // move files from source dir to target directory - $copy_job = $this->task_factory->createTask(ilCopyFilesToTempDirectoryJob::class, [$file_size_interaction]); + $copy_job = $this->task_factory->createTask(CopyFilesToTempDirectoryJob::class, [$file_size_interaction]); // Zip it - $zip_job = $this->task_factory->createTask(ilZipJob::class, [$copy_job]); + $zip_job = $this->task_factory->createTask(ZipJob::class, [$copy_job]); $this->logger->debug('Moved files from source- to target-directory'); // Download $download_name = new StringValue(); $download_name->setValue($normalized_name . '.zip'); - $download_interaction = $this->task_factory->createTask(ilDownloadZipInteraction::class, [$zip_job, $download_name]); + $download_interaction = $this->task_factory->createTask(DownloadZipInteraction::class, [$zip_job, $download_name]); $this->logger->debug('Created a download interaction with the following download name: ' . $download_name->getValue()); // last task to bucket diff --git a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCheckSumOfFileSizesJob.php b/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php similarity index 90% rename from components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCheckSumOfFileSizesJob.php rename to components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php index 8d6dc8fa8ce2..89c4c5c4ff2b 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCheckSumOfFileSizesJob.php +++ b/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Observer; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractJob; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\BooleanValue; @@ -30,9 +32,9 @@ * @author Lukas Zehnder * */ -class ilCheckSumOfFileSizesJob extends AbstractJob +class CheckSumOfFileSizesJob extends AbstractJob { - private readonly ?ilLogger $logger; + private readonly ?\ilLogger $logger; protected \ilSetting $settings; @@ -43,7 +45,7 @@ public function __construct() { global $DIC; $this->logger = $DIC->logger()->cal(); - $this->settings = new ilSetting("fold"); + $this->settings = new \ilSetting("fold"); } @@ -54,7 +56,7 @@ public function getInputTypes(): array { return [ - new SingleType(ilCopyDefinition::class), + new SingleType(CopyDefinition::class), ]; } @@ -64,7 +66,7 @@ public function getInputTypes(): array */ public function getOutputType(): Type { - return new SingleType(ilCopyDefinition::class); + return new SingleType(CopyDefinition::class); } @@ -129,7 +131,7 @@ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void } // we are only interested in folders and files - switch (ilObject::_lookupType($ref_id, true)) { + switch (\ilObject::_lookupType($ref_id, true)) { case "fold": // get child objects $subtree = $tree->getChildsByTypeFilter($ref_id, ["fold", "file"]); @@ -143,7 +145,7 @@ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void break; case "file": - $a_file_size += ilObjFileAccess::_lookupFileSize($ref_id); + $a_file_size += \ilObjFileAccess::_lookupFileSize($ref_id); break; } } @@ -163,7 +165,7 @@ protected function validateAccess(int $ref_id): bool if (!$ilAccess->checkAccess("read", "", $ref_id)) { return false; } - return !ilObject::_isInTrash($ref_id); + return !\ilObject::_isInTrash($ref_id); } diff --git a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCollectFilesJob.php b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php similarity index 92% rename from components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCollectFilesJob.php rename to components/ILIAS/FileServices/src/Download/CollectFilesJob.php index ab4901c8a11e..bee9001785fa 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCollectFilesJob.php +++ b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Observer; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractJob; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\BooleanValue; @@ -29,14 +31,14 @@ * @author Lukas Zehnder * */ -class ilCollectFilesJob extends AbstractJob +class CollectFilesJob extends AbstractJob { - use ilObjFileSecureString; + use \ilObjFileSecureString; - private ?ilLogger $logger; + private ?\ilLogger $logger; /** * Holds the target mapped to the number of duplicates. - * @see ilCollectFilesJob::getFileDirs() + * @see CollectFilesJob::getFileDirs() * @var array */ private static array $targets = []; @@ -58,7 +60,7 @@ public function getInputTypes(): array { return [ - new SingleType(ilCopyDefinition::class), + new SingleType(CopyDefinition::class), new SingleType(BooleanValue::class), ]; } @@ -69,7 +71,7 @@ public function getInputTypes(): array */ public function getOutputType(): Type { - return new SingleType(ilCopyDefinition::class); + return new SingleType(CopyDefinition::class); } @@ -96,8 +98,8 @@ public function run(array $input, Observer $observer): Value $files = []; foreach ($object_ref_ids as $object_ref_id) { - $object = ilObjectFactory::getInstanceByRefId($object_ref_id); - if (!$object instanceof ilObject) { + $object = \ilObjectFactory::getInstanceByRefId($object_ref_id); + if (!$object instanceof \ilObject) { continue; } @@ -163,7 +165,7 @@ private function getFileDirs($a_ref_id, string $a_file_name, string $a_temp_dir) $user = $DIC->user(); $ilAccess = $DIC->access(); if ($ilAccess->checkAccessOfUser($user->getId(), "read", "", $a_ref_id)) { - $file = new ilObjFile($a_ref_id); + $file = new \ilObjFile($a_ref_id); $source_dir = $file->getFile(); if (@!is_file($source_dir)) { @@ -175,7 +177,7 @@ private function getFileDirs($a_ref_id, string $a_file_name, string $a_temp_dir) // invalid on Windows and prevent the archive from being extracted. They are // sanitized the same way as the folder names in recurseFolder(). $filname_with_suffix = $this->ensureSuffix( - ilFileUtils::getASCIIFilename($a_file_name), + \ilFileUtils::getASCIIFilename($a_file_name), $file->getFileExtension() ); @@ -227,7 +229,7 @@ private function recurseFolder($a_ref_id, string $a_folder_name, string $a_temp_ if ($num_recursions <= 1 && $a_initiated_by_folder_action) { $temp_dir = $a_temp_dir; } else { - $temp_dir = $a_temp_dir . '/' . ilFileUtils::getASCIIFilename($a_folder_name); + $temp_dir = $a_temp_dir . '/' . \ilFileUtils::getASCIIFilename($a_folder_name); } $subtree = $tree->getChildsByTypeFilter($a_ref_id, ["fold", "file"]); @@ -236,7 +238,7 @@ private function recurseFolder($a_ref_id, string $a_folder_name, string $a_temp_ if (!$ilAccess->checkAccess("read", "", $child["ref_id"])) { continue; } - if (ilObject::_isInTrash($child["ref_id"])) { + if (\ilObject::_isInTrash($child["ref_id"])) { continue; } if ($child["type"] == "fold") { diff --git a/components/ILIAS/BackgroundTasks_/classes/class.ilCopyDefinition.php b/components/ILIAS/FileServices/src/Download/CopyDefinition.php similarity index 98% rename from components/ILIAS/BackgroundTasks_/classes/class.ilCopyDefinition.php rename to components/ILIAS/FileServices/src/Download/CopyDefinition.php index 832ec09e79d5..f011662f63b9 100755 --- a/components/ILIAS/BackgroundTasks_/classes/class.ilCopyDefinition.php +++ b/components/ILIAS/FileServices/src/Download/CopyDefinition.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Implementation\Values\AbstractValue; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\BooleanValue; use ILIAS\BackgroundTasks\Value; @@ -24,7 +26,7 @@ * Description of class class * @author Stefan Meyer */ -class ilCopyDefinition extends AbstractValue +class CopyDefinition extends AbstractValue { public const COPY_SOURCE_DIR = 'source'; public const COPY_TARGET_DIR = 'target'; diff --git a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCopyFilesToTempDirectoryJob.php b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php similarity index 88% rename from components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCopyFilesToTempDirectoryJob.php rename to components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php index d2b22fca2c0e..6dc53c771b50 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilCopyFilesToTempDirectoryJob.php +++ b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractJob; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\StringValue; use ILIAS\BackgroundTasks\Observer; @@ -29,9 +31,9 @@ * @author Stefan Meyer * */ -class ilCopyFilesToTempDirectoryJob extends AbstractJob +class CopyFilesToTempDirectoryJob extends AbstractJob { - private ?ilLogger $logger; + private ?\ilLogger $logger; protected string $target_directory = ''; @@ -52,7 +54,7 @@ public function getInputTypes(): array { return [ - new SingleType(ilCopyDefinition::class), + new SingleType(CopyDefinition::class), ]; } @@ -111,8 +113,8 @@ public function run(array $input, Observer $observer): Value */ protected function createUniqueTempDirectory(): string { - $tmpdir = ilFileUtils::ilTempnam(); - ilFileUtils::makeDirParents($tmpdir); + $tmpdir = \ilFileUtils::ilTempnam(); + \ilFileUtils::makeDirParents($tmpdir); $this->logger->info('New temp directory: ' . $tmpdir); return $tmpdir; @@ -122,7 +124,7 @@ protected function createUniqueTempDirectory(): string protected function createTargetDirectory(string $a_tmpdir): string { $final_dir = $a_tmpdir . "/" . $this->target_directory; - ilFileUtils::makeDirParents($final_dir); + \ilFileUtils::makeDirParents($final_dir); $this->logger->info('New final directory: ' . $final_dir); return $final_dir; @@ -132,17 +134,17 @@ protected function createTargetDirectory(string $a_tmpdir): string /** * Copy files */ - protected function copyFiles(string $tmpdir, ilCopyDefinition $definition): void + protected function copyFiles(string $tmpdir, CopyDefinition $definition): void { foreach ($definition->getCopyDefinitions() as $copy_task) { - $source_dir_or_file = $copy_task[ilCopyDefinition::COPY_SOURCE_DIR]; - $target_dir_or_file = $copy_task[ilCopyDefinition::COPY_TARGET_DIR]; + $source_dir_or_file = $copy_task[CopyDefinition::COPY_SOURCE_DIR]; + $target_dir_or_file = $copy_task[CopyDefinition::COPY_TARGET_DIR]; $absolute_path_of_target_dir_or_file = $tmpdir . '/' . $target_dir_or_file; $absolute_directory_of_target_dir_or_file = $tmpdir . '/' . dirname($target_dir_or_file); $this->logger->debug('Creating directory: ' . $tmpdir . '/' . dirname($target_dir_or_file)); - ilFileUtils::makeDirParents( + \ilFileUtils::makeDirParents( $absolute_directory_of_target_dir_or_file ); diff --git a/components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilDownloadZipInteraction.php b/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php similarity index 92% rename from components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilDownloadZipInteraction.php rename to components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php index 0b0bc5123edc..3545d3d56587 100755 --- a/components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilDownloadZipInteraction.php +++ b/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Bucket; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractUserInteraction; use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionOption; @@ -33,11 +35,11 @@ * @author Stefan Meyer * */ -class ilDownloadZipInteraction extends AbstractUserInteraction +class DownloadZipInteraction extends AbstractUserInteraction { public const OPTION_DOWNLOAD = 'download'; public const OPTION_CANCEL = 'cancel'; - private ?ilLogger $logger; + private ?\ilLogger $logger; public function __construct() @@ -108,7 +110,7 @@ public function interaction(array $input, Option $user_selected_option, Bucket $ try { $path = LegacyPathHelper::createRelativePath($zip_name->getValue()); - } catch (InvalidArgumentException) { + } catch (\InvalidArgumentException) { $path = null; } if (!is_null($path) && $filesystem->has($path)) { @@ -120,7 +122,7 @@ public function interaction(array $input, Option $user_selected_option, Bucket $ $this->logger->info("Delivering File."); - ilFileDelivery::deliverFileAttached($download_name->getValue(), $zip_name->getValue()); + \ilFileDelivery::deliverFileAttached($download_name->getValue(), $zip_name->getValue()); return new ThunkValue(); } diff --git a/components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilSumOfFileSizesTooLargeInteraction.php b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php similarity index 91% rename from components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilSumOfFileSizesTooLargeInteraction.php rename to components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php index 5ceb63d8a038..e8c067f6149a 100755 --- a/components/ILIAS/BackgroundTasks_/classes/UserInteractions/class.ilSumOfFileSizesTooLargeInteraction.php +++ b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\BackgroundTasks\Bucket; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractUserInteraction; use ILIAS\BackgroundTasks\Implementation\Tasks\UserInteraction\UserInteractionOption; @@ -25,18 +27,17 @@ use ILIAS\BackgroundTasks\Value; /** - * Class ilSumOfFileSizesTooLargeInteraction * @author Lukas Zehnder * @author Fabian Schmid */ -class ilSumOfFileSizesTooLargeInteraction extends AbstractUserInteraction +class SumOfFileSizesTooLargeInteraction extends AbstractUserInteraction { /** * @var string */ private const OPTION_OK = 'ok'; - protected ilLanguage $lng; + protected \ilLanguage $lng; public function __construct() { @@ -51,13 +52,13 @@ public function __construct() public function getInputTypes(): array { return [ - new SingleType(ilCopyDefinition::class), + new SingleType(CopyDefinition::class), ]; } public function getOutputType(): Type { - return new SingleType(ilCopyDefinition::class); + return new SingleType(CopyDefinition::class); } #[\Override] diff --git a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilZipJob.php b/components/ILIAS/FileServices/src/Download/ZipJob.php similarity index 94% rename from components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilZipJob.php rename to components/ILIAS/FileServices/src/Download/ZipJob.php index 6d78dc33efd1..ea723b998a37 100755 --- a/components/ILIAS/BackgroundTasks_/classes/Jobs/class.ilZipJob.php +++ b/components/ILIAS/FileServices/src/Download/ZipJob.php @@ -16,6 +16,8 @@ * *********************************************************************/ +namespace ILIAS\FileServices\Download; + use ILIAS\Filesystem\Util\Archive\Archives; use ILIAS\BackgroundTasks\Observer; use ILIAS\BackgroundTasks\Implementation\Tasks\AbstractJob; @@ -30,9 +32,9 @@ * @author Stefan Meyer * */ -class ilZipJob extends AbstractJob +class ZipJob extends AbstractJob { - private ?ilLogger $logger; + private ?\ilLogger $logger; private Archives $archives; /** @@ -95,7 +97,7 @@ public function run(array $input, Observer $observer): Value stream_copy_to_stream($zip_stream->detach(), fopen($zip_name, 'wb')); // we currently have to write to disk // delete temp directory - ilFileUtils::delDir($tmpdir); + \ilFileUtils::delDir($tmpdir); $zip_file_name = new StringValue(); $zip_file_name->setValue($zip_name); diff --git a/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php b/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php index 234b9300c18a..5d11e8217a60 100755 --- a/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php +++ b/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php @@ -18,6 +18,8 @@ use ILIAS\BackgroundTasks\Implementation\Bucket\BasicBucket; use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\StringValue; +use ILIAS\FileServices\Download\DownloadZipInteraction; +use ILIAS\FileServices\Download\ZipJob; /** * Background task for downloads @@ -103,13 +105,13 @@ public function run(): bool $copy_job = $this->task_factory->createTask(ilCopyWorkspaceFilesToTempDirectoryJob::class, [$file_size_interaction]); // Zip it - $zip_job = $this->task_factory->createTask(ilZipJob::class, [$copy_job]); + $zip_job = $this->task_factory->createTask(ZipJob::class, [$copy_job]); $this->logger->debug('Moved files from source- to target-directory'); // Download $download_name = new StringValue(); $download_name->setValue($normalized_name . '.zip'); - $download_interaction = $this->task_factory->createTask(ilDownloadZipInteraction::class, [$zip_job, $download_name]); + $download_interaction = $this->task_factory->createTask(DownloadZipInteraction::class, [$zip_job, $download_name]); $this->logger->debug('Created a download interaction with the following download name: ' . $download_name->getValue()); // last task to bucket From 0c69649cd7feb9d9394d4d582905e241feefd157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 11:51:37 +0100 Subject: [PATCH 05/14] FileServices: Remove comments that restate the download job code The moved download jobs carried placeholder class docblocks and step comments that repeat the next line. CollectFilesJob::$targets becomes $duplicate_count_by_target, so the name says what the deleted comment said. --- ...ss.ilDownloadContainerFilesBackgroundTask.php | 6 ------ .../src/Download/CheckSumOfFileSizesJob.php | 7 ------- .../src/Download/CollectFilesJob.php | 16 ++++------------ .../FileServices/src/Download/CopyDefinition.php | 4 ---- .../src/Download/CopyFilesToTempDirectoryJob.php | 9 --------- .../src/Download/DownloadZipInteraction.php | 6 ------ .../SumOfFileSizesTooLargeInteraction.php | 4 ---- .../ILIAS/FileServices/src/Download/ZipJob.php | 7 ------- ...s.ilDownloadWorkspaceFolderBackgroundTask.php | 1 - 9 files changed, 4 insertions(+), 56 deletions(-) diff --git a/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php b/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php index f0cc69868b57..baa5ad27e2c6 100755 --- a/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php +++ b/components/ILIAS/Container/classes/BackgroundTasks/class.ilDownloadContainerFilesBackgroundTask.php @@ -89,29 +89,23 @@ public function run(): bool $bucket->setUserId($this->user_id); $this->logger->debug('Created bucket and set the following user id: ' . $this->user_id); - // Copy Definition $definition = new CopyDefinition(); $normalized_name = ilFileUtils::getASCIIFilename($this->getBucketTitle()); $definition->setTempDir($normalized_name); $definition->setObjectRefIds($this->object_ref_ids); $this->logger->debug('Created copy definition and added the following tempdir: ' . $normalized_name); - // Collect all files by the definition and prevent duplicates $collect_job = $this->task_factory->createTask(CollectFilesJob::class, [$definition, $this->initiated_by_folder_action]); $this->logger->debug('Collected files based on the following object ids: '); $this->logger->dump($this->object_ref_ids); - // Check the FileSize $file_size_job = $this->task_factory->createTask(CheckSumOfFileSizesJob::class, [$collect_job]); - // Show problems with file-limit $file_size_interaction = $this->task_factory->createTask(SumOfFileSizesTooLargeInteraction::class, [$file_size_job]); $this->logger->debug('Determined the sum of all file sizes'); - // move files from source dir to target directory $copy_job = $this->task_factory->createTask(CopyFilesToTempDirectoryJob::class, [$file_size_interaction]); - // Zip it $zip_job = $this->task_factory->createTask(ZipJob::class, [$copy_job]); $this->logger->debug('Moved files from source- to target-directory'); diff --git a/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php b/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php index 89c4c5c4ff2b..ba8d1a462959 100755 --- a/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php +++ b/components/ILIAS/FileServices/src/Download/CheckSumOfFileSizesJob.php @@ -26,12 +26,6 @@ use ILIAS\BackgroundTasks\Value; use ILIAS\components\File\Settings\General; -/** - * Description of class class - * - * @author Lukas Zehnder - * - */ class CheckSumOfFileSizesJob extends AbstractJob { private readonly ?\ilLogger $logger; @@ -130,7 +124,6 @@ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void continue; } - // we are only interested in folders and files switch (\ilObject::_lookupType($ref_id, true)) { case "fold": // get child objects diff --git a/components/ILIAS/FileServices/src/Download/CollectFilesJob.php b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php index bee9001785fa..92fbbde06aa0 100755 --- a/components/ILIAS/FileServices/src/Download/CollectFilesJob.php +++ b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php @@ -25,23 +25,15 @@ use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; -/** - * Description of class class - * - * @author Lukas Zehnder - * - */ class CollectFilesJob extends AbstractJob { use \ilObjFileSecureString; private ?\ilLogger $logger; /** - * Holds the target mapped to the number of duplicates. - * @see CollectFilesJob::getFileDirs() * @var array */ - private static array $targets = []; + private static array $duplicate_count_by_target = []; /** * Construct @@ -186,16 +178,16 @@ private function getFileDirs($a_ref_id, string $a_file_name, string $a_temp_dir) // #25025: allow duplicate filenames by appending an incrementing // number per duplicate in brackets to the name. // Example: test.txt, test (1).txt, test (2).txt, ... - if (isset(self::$targets[$target_dir])) { + if (isset(self::$duplicate_count_by_target[$target_dir])) { $target_info = pathinfo($target_dir); $filename = $target_info["filename"]; $extension = isset($target_info["extension"]) ? "." . $target_info["extension"] : ""; // pathinfo() returns the name without its directory, therefore the separator // has to be added again, just like above $target_dir = $a_temp_dir . '/' . $filename - . " (" . ++self::$targets[$target_dir] . ")" . $extension; + . " (" . ++self::$duplicate_count_by_target[$target_dir] . ")" . $extension; } else { - self::$targets[$target_dir] = 0; + self::$duplicate_count_by_target[$target_dir] = 0; } return [ diff --git a/components/ILIAS/FileServices/src/Download/CopyDefinition.php b/components/ILIAS/FileServices/src/Download/CopyDefinition.php index f011662f63b9..6fd9a8ecfc33 100755 --- a/components/ILIAS/FileServices/src/Download/CopyDefinition.php +++ b/components/ILIAS/FileServices/src/Download/CopyDefinition.php @@ -22,10 +22,6 @@ use ILIAS\BackgroundTasks\Implementation\Values\ScalarValues\BooleanValue; use ILIAS\BackgroundTasks\Value; -/** - * Description of class class - * @author Stefan Meyer - */ class CopyDefinition extends AbstractValue { public const COPY_SOURCE_DIR = 'source'; diff --git a/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php index 6dc53c771b50..fb000cdaaed8 100755 --- a/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php +++ b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php @@ -25,12 +25,6 @@ use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; -/** - * Description of class class - * - * @author Stefan Meyer - * - */ class CopyFilesToTempDirectoryJob extends AbstractJob { private ?\ilLogger $logger; @@ -131,9 +125,6 @@ protected function createTargetDirectory(string $a_tmpdir): string } - /** - * Copy files - */ protected function copyFiles(string $tmpdir, CopyDefinition $definition): void { foreach ($definition->getCopyDefinitions() as $copy_task) { diff --git a/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php b/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php index 3545d3d56587..50d87506651d 100755 --- a/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php +++ b/components/ILIAS/FileServices/src/Download/DownloadZipInteraction.php @@ -29,12 +29,6 @@ use ILIAS\BackgroundTasks\Value; use ILIAS\Filesystem\Util\LegacyPathHelper; -/** - * Description of class class - * - * @author Stefan Meyer - * - */ class DownloadZipInteraction extends AbstractUserInteraction { public const OPTION_DOWNLOAD = 'download'; diff --git a/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php index e8c067f6149a..a50f60b2d90d 100755 --- a/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php +++ b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php @@ -26,10 +26,6 @@ use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; -/** - * @author Lukas Zehnder - * @author Fabian Schmid - */ class SumOfFileSizesTooLargeInteraction extends AbstractUserInteraction { /** diff --git a/components/ILIAS/FileServices/src/Download/ZipJob.php b/components/ILIAS/FileServices/src/Download/ZipJob.php index ea723b998a37..079904a9bd13 100755 --- a/components/ILIAS/FileServices/src/Download/ZipJob.php +++ b/components/ILIAS/FileServices/src/Download/ZipJob.php @@ -26,12 +26,6 @@ use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; -/** - * Description of class class - * - * @author Stefan Meyer - * - */ class ZipJob extends AbstractJob { private ?\ilLogger $logger; @@ -96,7 +90,6 @@ public function run(array $input, Observer $observer): Value $zip_stream = $zip->get(); stream_copy_to_stream($zip_stream->detach(), fopen($zip_name, 'wb')); // we currently have to write to disk - // delete temp directory \ilFileUtils::delDir($tmpdir); $zip_file_name = new StringValue(); diff --git a/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php b/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php index 5d11e8217a60..8a1299c540b7 100755 --- a/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php +++ b/components/ILIAS/WorkspaceFolder/BackgroundTask/classes/class.ilDownloadWorkspaceFolderBackgroundTask.php @@ -104,7 +104,6 @@ public function run(): bool // move files from source dir to target directory $copy_job = $this->task_factory->createTask(ilCopyWorkspaceFilesToTempDirectoryJob::class, [$file_size_interaction]); - // Zip it $zip_job = $this->task_factory->createTask(ZipJob::class, [$copy_job]); $this->logger->debug('Moved files from source- to target-directory'); From 9401913b197a41a006449286cd583cbeeebfad43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 11:59:25 +0100 Subject: [PATCH 06/14] FileServices: Rewrite stored class names of the moved download jobs Background task buckets store the class name of each task and value in il_bt_task and il_bt_value. Without this step, the async worker cannot load a scheduled ZIP download that names ilCopyDefinition. It stops on that bucket at every run and leaves it in the queue. The new database update step replaces the seven old names with the new ones in the class_name and type columns. --- .../class.ilFileServicesDatabaseObjective.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php b/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php index 464615d0a01a..fc10677d10df 100755 --- a/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php +++ b/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php @@ -103,6 +103,39 @@ public function step_1(): void $this->database->createSequence("il_upload_policy"); } + /** + * Stored background task buckets reference their tasks and values by class name. + */ + public function step_2(): void + { + $this->abortIfNotPrepared(); + + $moved_classes = [ + 'ilCopyDefinition' => 'ILIAS\FileServices\Download\CopyDefinition', + 'ilCollectFilesJob' => 'ILIAS\FileServices\Download\CollectFilesJob', + 'ilCheckSumOfFileSizesJob' => 'ILIAS\FileServices\Download\CheckSumOfFileSizesJob', + 'ilCopyFilesToTempDirectoryJob' => 'ILIAS\FileServices\Download\CopyFilesToTempDirectoryJob', + 'ilZipJob' => 'ILIAS\FileServices\Download\ZipJob', + 'ilSumOfFileSizesTooLargeInteraction' => 'ILIAS\FileServices\Download\SumOfFileSizesTooLargeInteraction', + 'ilDownloadZipInteraction' => 'ILIAS\FileServices\Download\DownloadZipInteraction', + ]; + + foreach (['il_bt_task', 'il_bt_value'] as $table) { + if (!$this->database->tableExists($table)) { + continue; + } + foreach (['class_name', 'type'] as $column) { + foreach ($moved_classes as $old_name => $new_name) { + $this->database->manipulateF( + "UPDATE $table SET $column = %s WHERE $column = %s", + ['text', 'text'], + [$new_name, $old_name] + ); + } + } + } + } + /** * @throws LogicException if the database update steps were not * yet prepared. From 74e7703953f32a7d70c58c0f1445e9e5531c6fc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 12:45:55 +0100 Subject: [PATCH 07/14] BackgroundTasks: Delete the BackgroundTasks_ shell component The setup, SOAP, notification UI, and download job classes have moved out of BackgroundTasks_. Only an empty Component class, PRIVACY.md, and ROADMAP.md were left. cli/build_bootstrap.php discovers components by directory, so deleting the directory removes the component. The setup label in ConfigStoredObjective and the IRSS storage audit now name BackgroundTasks. --- .../src/Setup/ConfigStoredObjective.php | 2 +- .../BackgroundTasks_/BackgroundTasks_.php | 38 ------------------- components/ILIAS/BackgroundTasks_/PRIVACY.md | 8 ---- components/ILIAS/BackgroundTasks_/ROADMAP.md | 3 -- .../ResourceStorage/IRSS_STORAGE_AUDIT.md | 2 +- 5 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 components/ILIAS/BackgroundTasks_/BackgroundTasks_.php delete mode 100644 components/ILIAS/BackgroundTasks_/PRIVACY.md delete mode 100755 components/ILIAS/BackgroundTasks_/ROADMAP.md diff --git a/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php index 2011b7a29590..627921ddd046 100755 --- a/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php +++ b/components/ILIAS/BackgroundTasks/src/Setup/ConfigStoredObjective.php @@ -36,7 +36,7 @@ public function getHash(): string public function getLabel(): string { - return "Fill ini with settings for components/ILIAS/BackgroundTasks_"; + return "Fill ini with settings for components/ILIAS/BackgroundTasks"; } public function isNotable(): bool diff --git a/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php b/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php deleted file mode 100644 index 2e4e707e4b00..000000000000 --- a/components/ILIAS/BackgroundTasks_/BackgroundTasks_.php +++ /dev/null @@ -1,38 +0,0 @@ - Date: Tue, 27 Oct 2026 12:46:00 +0100 Subject: [PATCH 08/14] Folder: Remove the ilCtrl call to ilBackgroundTaskHub No class ilBackgroundTaskHub exists in the tree. The @ilCtrl_Calls entry on ilObjFolderGUI pointed at nothing. --- components/ILIAS/Folder/classes/class.ilObjFolderGUI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ILIAS/Folder/classes/class.ilObjFolderGUI.php b/components/ILIAS/Folder/classes/class.ilObjFolderGUI.php index ccae1487dd52..f070af859284 100755 --- a/components/ILIAS/Folder/classes/class.ilObjFolderGUI.php +++ b/components/ILIAS/Folder/classes/class.ilObjFolderGUI.php @@ -31,7 +31,7 @@ * @ilCtrl_Calls ilObjFolderGUI: ilInfoScreenGUI, ilContainerPageGUI, ilColumnGUI * @ilCtrl_Calls ilObjFolderGUI: ilObjectCopyGUI, ilObjectContentStyleSettingsGUI * @ilCtrl_Calls ilObjFolderGUI: ilExportGUI, ilCommonActionDispatcherGUI, ilDidacticTemplateGUI - * @ilCtrl_Calls ilObjFolderGUI: ilBackgroundTaskHub, ILIAS\ILIASObject\Properties\Translations\TranslationGUI, ilRepositoryTrashGUI + * @ilCtrl_Calls ilObjFolderGUI: ILIAS\ILIASObject\Properties\Translations\TranslationGUI, ilRepositoryTrashGUI */ class ilObjFolderGUI extends ilContainerGUI { From f7e3b6820dc602e973ad74639193bae223753836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 12:49:31 +0100 Subject: [PATCH 09/14] docs(BackgroundTasks): drop the BackgroundTasks_ maintenance entry The component directory is gone, so the authority block that pointed at it is gone too. --- docs/development/maintenance.md | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/docs/development/maintenance.md b/docs/development/maintenance.md index 0d54725f5c55..74014fe2594e 100755 --- a/docs/development/maintenance.md +++ b/docs/development/maintenance.md @@ -420,22 +420,6 @@ Components are listed alphabetically by their folder name in `components/ILIAS/` [//]: # (END BackgroundTasks) -[//]: # (BEGIN BackgroundTasks_) - -#### [BackgroundTasks_](https://github.com/ILIAS-eLearning/ILIAS/tree/trunk/components/ILIAS/BackgroundTasks_) - -*Belongs to:* [Background Tasks](https://docu.ilias.de/go/wiki/wpage_4383_1357) - -* Authority to Sign off on Conceptual Changes: [thojou](https://docu.ilias.de/go/usr/103745), [mjansen](https://docu.ilias.de/go/usr/8784) -* Authority to Sign off on Code Changes: [thojou](https://docu.ilias.de/go/usr/103745), [mjansen](https://docu.ilias.de/go/usr/8784) -* Authority to Curate Test Cases: MISSING -* Authority to (De-)Assign Authorities: [thojou (Databay AG)](https://docu.ilias.de/go/usr/103745) -* Assignee for Issues: [thojou](https://docu.ilias.de/go/usr/103745) -* Assignee for Security Reports: [thojou](https://docu.ilias.de/go/usr/103745) - -[//]: # (END BackgroundTasks_) - - [//]: # (BEGIN Badge) #### [Badge](https://github.com/ILIAS-eLearning/ILIAS/tree/trunk/components/ILIAS/Badge) From d4cd16e29c1aeadabf0893d9fdbe88df33025c37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Tue, 27 Oct 2026 13:50:24 +0100 Subject: [PATCH 10/14] fix(BackgroundTasks): add the GPL header to background_task_refresh.js The copyright checker requires this header at the top of every JS file. The moved refresh script never had one. --- .../resources/background_task_refresh.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/components/ILIAS/BackgroundTasks/resources/background_task_refresh.js b/components/ILIAS/BackgroundTasks/resources/background_task_refresh.js index f7a41959b735..4ee279ef8bb4 100644 --- a/components/ILIAS/BackgroundTasks/resources/background_task_refresh.js +++ b/components/ILIAS/BackgroundTasks/resources/background_task_refresh.js @@ -1,3 +1,18 @@ +/** + * This file is part of ILIAS, a powerful learning management system + * published by ILIAS open source e-Learning e.V. + * + * ILIAS is licensed with the GPL-3.0, + * see https://www.gnu.org/licenses/gpl-3.0.en.html + * You should have received a copy of said license along with the + * source code, too. + * + * If this is not the case or you just want to try ILIAS, you'll find + * us at: + * https://www.ilias.de + * https://github.com/ILIAS-eLearning + */ + il = il || {}; il.BGTask = il.BGTask || {}; (function ($, il) { From 24c248ccc9c48e9113845271f453e80d324d932d Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Tue, 27 Oct 2026 16:57:28 +0100 Subject: [PATCH 11/14] Revert "FileServices: Rewrite stored class names of the moved download jobs" This reverts commit 869199b739ecaf782aa8388b35c8168b54f53bf1. --- .../class.ilFileServicesDatabaseObjective.php | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php b/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php index fc10677d10df..464615d0a01a 100755 --- a/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php +++ b/components/ILIAS/FileServices/classes/Setup/Database/class.ilFileServicesDatabaseObjective.php @@ -103,39 +103,6 @@ public function step_1(): void $this->database->createSequence("il_upload_policy"); } - /** - * Stored background task buckets reference their tasks and values by class name. - */ - public function step_2(): void - { - $this->abortIfNotPrepared(); - - $moved_classes = [ - 'ilCopyDefinition' => 'ILIAS\FileServices\Download\CopyDefinition', - 'ilCollectFilesJob' => 'ILIAS\FileServices\Download\CollectFilesJob', - 'ilCheckSumOfFileSizesJob' => 'ILIAS\FileServices\Download\CheckSumOfFileSizesJob', - 'ilCopyFilesToTempDirectoryJob' => 'ILIAS\FileServices\Download\CopyFilesToTempDirectoryJob', - 'ilZipJob' => 'ILIAS\FileServices\Download\ZipJob', - 'ilSumOfFileSizesTooLargeInteraction' => 'ILIAS\FileServices\Download\SumOfFileSizesTooLargeInteraction', - 'ilDownloadZipInteraction' => 'ILIAS\FileServices\Download\DownloadZipInteraction', - ]; - - foreach (['il_bt_task', 'il_bt_value'] as $table) { - if (!$this->database->tableExists($table)) { - continue; - } - foreach (['class_name', 'type'] as $column) { - foreach ($moved_classes as $old_name => $new_name) { - $this->database->manipulateF( - "UPDATE $table SET $column = %s WHERE $column = %s", - ['text', 'text'], - [$new_name, $old_name] - ); - } - } - } - } - /** * @throws LogicException if the database update steps were not * yet prepared. From c728ebc73a35970214632476effff4d440501e37 Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Tue, 27 Oct 2026 17:57:31 +0100 Subject: [PATCH 12/14] BackgroundTasks: Let components contribute tasks and values via bootstrap Buckets store their tasks and values by class name, so BackgroundTasks depends on classes of other components without that dependency being visible anywhere. Components now contribute their tasks and values as ILIAS\BackgroundTasks\Task and ILIAS\BackgroundTasks\Value. The Registry keeps the contributions as prototypes, and the task factory and the persistence clone them instead of building them through the injector. Classes nobody contributed, e.g. tasks of plugins, are still built through the injector. Init populates the Registry as $DIC['bt.registry']. --- .../ILIAS/BackgroundTasks/BackgroundTasks.php | 25 ++++++ components/ILIAS/BackgroundTasks/ROADMAP.md | 29 +++++++ .../src/BackgroundTaskServices.php | 6 ++ .../Persistence/BasicPersistence.php | 5 +- .../Implementation/Registry/BasicRegistry.php | 84 +++++++++++++++++++ .../Implementation/Tasks/BasicTaskFactory.php | 17 ++-- .../BackgroundTasks/src/Registry/Registry.php | 55 ++++++++++++ components/ILIAS/BackgroundTasks/src/Task.php | 6 ++ .../Tasks/BasicTaskFactoryTest.php | 20 ++++- components/ILIAS/Init/Init.php | 1 + .../Init/classes/class.ilInitialisation.php | 12 ++- .../ILIAS/Init/src/AllModernComponents.php | 3 + 12 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 components/ILIAS/BackgroundTasks/ROADMAP.md create mode 100644 components/ILIAS/BackgroundTasks/src/Implementation/Registry/BasicRegistry.php create mode 100644 components/ILIAS/BackgroundTasks/src/Registry/Registry.php diff --git a/components/ILIAS/BackgroundTasks/BackgroundTasks.php b/components/ILIAS/BackgroundTasks/BackgroundTasks.php index 418937558208..8f7e59d9219f 100644 --- a/components/ILIAS/BackgroundTasks/BackgroundTasks.php +++ b/components/ILIAS/BackgroundTasks/BackgroundTasks.php @@ -27,6 +27,12 @@ use ILIAS\Refinery\Factory; use ILIAS\Language\ComponentTranslation\LanguageFileDirectory; use ILIAS\Language\ComponentTranslation\ComponentLanguageFileDirectory; +use ILIAS\BackgroundTasks\Registry\Registry; +use ILIAS\BackgroundTasks\Task; +use ILIAS\BackgroundTasks\Value; +use ILIAS\BackgroundTasks\Implementation\Registry\BasicRegistry; +use ILIAS\BackgroundTasks\Implementation\Values; +use ILIAS\BackgroundTasks\Implementation\Tasks; class BackgroundTasks implements Component { @@ -40,6 +46,25 @@ public function init( array | \ArrayAccess &$pull, array | \ArrayAccess &$internal, ): void { + $define[] = Registry::class; + + // components putting tasks or values into buckets contribute them, see Registry\Registry + $implement[Registry::class] = static fn(): Registry => + new BasicRegistry( + $seek[Task::class], + $seek[Value::class] + ); + + $contribute[Value::class] = static fn() => new Values\ScalarValues\ScalarValue(); + $contribute[Value::class] = static fn() => new Values\ScalarValues\BooleanValue(); + $contribute[Value::class] = static fn() => new Values\ScalarValues\FloatValue(); + $contribute[Value::class] = static fn() => new Values\ScalarValues\IntegerValue(); + $contribute[Value::class] = static fn() => new Values\ScalarValues\StringValue(); + $contribute[Value::class] = static fn() => new Values\AggregationValues\ListValue(); + $contribute[Value::class] = static fn() => new Values\ThunkValue(); + $contribute[Task::class] = static fn() => new Tasks\Aggregation\ConcatenationJob(); + $contribute[Task::class] = static fn() => new Tasks\NotFoundUserInteraction(); + $contribute[Agent::class] = static fn(): Agent => new \ILIAS\BackgroundTasks\Setup\Agent( $pull[Factory::class] diff --git a/components/ILIAS/BackgroundTasks/ROADMAP.md b/components/ILIAS/BackgroundTasks/ROADMAP.md new file mode 100644 index 000000000000..291ac458d56f --- /dev/null +++ b/components/ILIAS/BackgroundTasks/ROADMAP.md @@ -0,0 +1,29 @@ +# Roadmap + +## Mid Term + +### Build all Tasks and Values from their Contributions + +Components contribute their tasks and values to the bootstrap, see `Registry\Registry`. The task +factory and the persistence clone these contributions. A task or value nobody contributed is still +built by the `Dependencies\Injector`, which resolves its constructor arguments from the legacy +container. This fallback is needed as long as not all components contribute their tasks and values, +and as long as plugins cannot contribute to the bootstrap at all. + +The goal is to build all tasks and values from their contributions and to remove the `Injector` +together with its `DependencyMap`. Before that, every component putting tasks or values into +buckets has to contribute them and pass their dependencies through the constructor, and plugins +need a way to contribute theirs. + +### Move the Initialisation out of ilInitialisation + +The task factory, the persistence, the task manager and the injector are still set up in +`ilInitialisation::initBackgroundTasks()` and `ilInitialisation::initInjector()` and reach the +rest of the system through the legacy container, while only the `Registry` comes from the bootstrap. +Scripts calling `ilInitialisation::initILIAS()` directly get an empty `Registry`. The goal is to +initialise BackgroundTasks completely through the component bootstrap, as described in +`components/ILIAS/Component/docs/component-bootstrap-migration.md`, and to bridge the services into +the legacy container through `AllModernComponents` as long as they are needed there. This includes +putting the settings read from `ilias.ini.php` (`concurrency`, `number_of_concurrent_tasks`) behind +a configuration interface and replacing the static `BasicPersistence::instance()` and the global +`$DIC` it uses. diff --git a/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php b/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php index 18f7e30b120b..e59ad6e9f890 100755 --- a/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php +++ b/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php @@ -23,6 +23,7 @@ use ILIAS\BackgroundTasks\TaskManager; use ILIAS\DI\Container; use ILIAS\BackgroundTasks\Dependencies\Injector; +use ILIAS\BackgroundTasks\Registry\Registry; /** * Class BackgroundTaskServices @@ -54,4 +55,9 @@ public function injector(): Injector { return $this->container['bt.injector']; } + + public function registry(): Registry + { + return $this->container['bt.registry']; + } } diff --git a/components/ILIAS/BackgroundTasks/src/Implementation/Persistence/BasicPersistence.php b/components/ILIAS/BackgroundTasks/src/Implementation/Persistence/BasicPersistence.php index a8441b33dc37..2f1251a7be0a 100755 --- a/components/ILIAS/BackgroundTasks/src/Implementation/Persistence/BasicPersistence.php +++ b/components/ILIAS/BackgroundTasks/src/Implementation/Persistence/BasicPersistence.php @@ -449,6 +449,7 @@ private function loadTask(int $taskContainerId, Bucket $bucket, BucketContainer private function loadValue($valueContainerId, Bucket $bucket, BucketContainer $bucketContainer): Value { global $DIC; + $registry = $DIC->backgroundTasks()->registry(); $factory = $DIC->backgroundTasks()->injector(); /** @var ValueContainer $valueContainer */ @@ -461,7 +462,9 @@ private function loadValue($valueContainerId, Bucket $bucket, BucketContainer $b /** @noinspection PhpIncludeInspection */ /** @var Value $value */ - $value = $factory->createInstance($valueContainer->getClassName()); + $value = $registry->isRegistered($valueContainer->getClassName()) + ? $registry->newValue($valueContainer->getClassName()) + : $factory->createInstance($valueContainer->getClassName()); $value->unserialize($valueContainer->getSerialized()); if ($valueContainer->getHasParenttask() !== 0) { diff --git a/components/ILIAS/BackgroundTasks/src/Implementation/Registry/BasicRegistry.php b/components/ILIAS/BackgroundTasks/src/Implementation/Registry/BasicRegistry.php new file mode 100644 index 000000000000..61702cb48d45 --- /dev/null +++ b/components/ILIAS/BackgroundTasks/src/Implementation/Registry/BasicRegistry.php @@ -0,0 +1,84 @@ + + */ + private array $tasks = []; + + /** + * @var array + */ + private array $values = []; + + /** + * @param Task[] $tasks + * @param Value[] $values + */ + public function __construct(array $tasks, array $values) + { + foreach ($tasks as $task) { + $this->tasks[$this->unique($task)] = $task; + } + foreach ($values as $value) { + $this->values[$this->unique($value)] = $value; + } + } + + public function isRegistered(string $class_name): bool + { + $class_name = ltrim($class_name, '\\'); + return isset($this->tasks[$class_name]) || isset($this->values[$class_name]); + } + + public function newTask(string $class_name): Task + { + $class_name = ltrim($class_name, '\\'); + if (!isset($this->tasks[$class_name])) { + throw new \OutOfBoundsException("No task $class_name is registered."); + } + return clone $this->tasks[$class_name]; + } + + public function newValue(string $class_name): Value + { + $class_name = ltrim($class_name, '\\'); + if (!isset($this->values[$class_name])) { + throw new \OutOfBoundsException("No value $class_name is registered."); + } + return clone $this->values[$class_name]; + } + + private function unique(Task|Value $prototype): string + { + $class_name = $prototype::class; + if (isset($this->tasks[$class_name]) || isset($this->values[$class_name])) { + throw new \LogicException("$class_name is contributed more than once."); + } + return $class_name; + } +} diff --git a/components/ILIAS/BackgroundTasks/src/Implementation/Tasks/BasicTaskFactory.php b/components/ILIAS/BackgroundTasks/src/Implementation/Tasks/BasicTaskFactory.php index 17fac97198cd..5473f7494c48 100755 --- a/components/ILIAS/BackgroundTasks/src/Implementation/Tasks/BasicTaskFactory.php +++ b/components/ILIAS/BackgroundTasks/src/Implementation/Tasks/BasicTaskFactory.php @@ -23,13 +23,16 @@ use ILIAS\BackgroundTasks\Task; use ILIAS\BackgroundTasks\Task\TaskFactory; use ILIAS\BackgroundTasks\Value; +use ILIAS\BackgroundTasks\Registry\Registry; class BasicTaskFactory implements TaskFactory { use BasicScalarValueFactory; - public function __construct(protected Injector $injector) - { + public function __construct( + protected Injector $injector, + protected Registry $registry + ) { } /** @@ -37,11 +40,15 @@ public function __construct(protected Injector $injector) */ public function createTask(string $class_name, ?array $input = null): Task { - if (!class_exists($class_name)) { + if ($this->registry->isRegistered($class_name)) { + $task = $this->registry->newTask($class_name); + } elseif (class_exists($class_name)) { + // not contributed (yet), e.g. tasks of plugins, see ROADMAP.md + /** @var Task $task */ + $task = $this->injector->createInstance($class_name); + } else { return new NotFoundUserInteraction(); } - /** @var Task $task */ - $task = $this->injector->createInstance($class_name); if ($input) { $wrappedInput = array_map(function ($i): Value { if ($i instanceof Task) { diff --git a/components/ILIAS/BackgroundTasks/src/Registry/Registry.php b/components/ILIAS/BackgroundTasks/src/Registry/Registry.php new file mode 100644 index 000000000000..7d69422c1752 --- /dev/null +++ b/components/ILIAS/BackgroundTasks/src/Registry/Registry.php @@ -0,0 +1,55 @@ +createTask(PlusJob::class, [1, 2]); $this->assertInstanceOf(\ILIAS\BackgroundTasks\Implementation\Tasks\PlusJob::class, $plusJob); $plusJobInput = $plusJob->getInput(); @@ -56,4 +57,21 @@ public function testBasicFactory(): void $plusJobInput = $plusJob->getInput(); $this->assertTrue($plusJobInput[0]->equals($one)); } + + public function testRegisteredTasksAreClonedFromTheirPrototype(): void + { + $prototype = new PlusJob(); + $injector = new Injector(new Container(), new BaseDependencyMap()); + $taskFactory = new BasicTaskFactory($injector, new BasicRegistry([$prototype], [])); + + $first = $taskFactory->createTask(PlusJob::class, [1, 2]); + $second = $taskFactory->createTask(PlusJob::class, [3, 4]); + + $this->assertNotSame($prototype, $first); + $this->assertNotSame($first, $second); + $this->assertSame([], $prototype->getInput()); + $three = new IntegerValue(); + $three->setValue(3); + $this->assertTrue($second->getInput()[0]->equals($three)); + } } diff --git a/components/ILIAS/Init/Init.php b/components/ILIAS/Init/Init.php index a59c26a87541..088aea09be09 100644 --- a/components/ILIAS/Init/Init.php +++ b/components/ILIAS/Init/Init.php @@ -187,6 +187,7 @@ public function init( $pull[\ILIAS\Language\Activities\AddLanguageEntry::class], $pull[\ILIAS\Language\Activities\SetLanguageDetectionEnabled::class], $pull[\ILIAS\Language\Activities\SetLanguageTranslationEnabled::class], + $use[\ILIAS\BackgroundTasks\Registry\Registry::class] ); $contribute[LanguageFileDirectory::class] = fn() => new ComponentLanguageFileDirectory( $this, diff --git a/components/ILIAS/Init/classes/class.ilInitialisation.php b/components/ILIAS/Init/classes/class.ilInitialisation.php index 73fbb819f093..b55e80e73a16 100755 --- a/components/ILIAS/Init/classes/class.ilInitialisation.php +++ b/components/ILIAS/Init/classes/class.ilInitialisation.php @@ -1706,8 +1706,18 @@ private static function initBackgroundTasks(\ILIAS\DI\Container $c): void $n_of_tasks = $n_of_tasks ?: 5; $sync = $sync ?: 'sync'; // The default value is sync. + // populated by AllModernComponents, empty for scripts calling initILIAS() directly + if (!isset($c["bt.registry"])) { + $c["bt.registry"] = function ($c) { + return new \ILIAS\BackgroundTasks\Implementation\Registry\BasicRegistry([], []); + }; + } + $c["bt.task_factory"] = function ($c) { - return new \ILIAS\BackgroundTasks\Implementation\Tasks\BasicTaskFactory($c["di.injector"]); + return new \ILIAS\BackgroundTasks\Implementation\Tasks\BasicTaskFactory( + $c["di.injector"], + $c["bt.registry"] + ); }; $c["bt.persistence"] = function ($c) { diff --git a/components/ILIAS/Init/src/AllModernComponents.php b/components/ILIAS/Init/src/AllModernComponents.php index 158758f0764f..62432f2e41b3 100644 --- a/components/ILIAS/Init/src/AllModernComponents.php +++ b/components/ILIAS/Init/src/AllModernComponents.php @@ -21,6 +21,7 @@ namespace ILIAS\Init; use ILIAS\KeyValueStorage\Services as KeyValueStorage; +use ILIAS\BackgroundTasks\Registry\Registry as BackgroundTasksRegistry; use ILIAS\WebDAV\Environment; use ILIAS\HTTP\GlobalHttpState; use ILIAS\FileDelivery\FileDeliveryServices; @@ -157,6 +158,7 @@ public function __construct( protected \ILIAS\Language\Activities\AddLanguageEntry $add_language_entry, protected \ILIAS\Language\Activities\SetLanguageDetectionEnabled $set_language_detection_enabled, protected \ILIAS\Language\Activities\SetLanguageTranslationEnabled $set_language_translation_enabled, + protected BackgroundTasksRegistry $background_tasks_registry ) { } @@ -279,6 +281,7 @@ protected function populateComponentsInLegacyEnvironment(\ILIAS\DI\Container $DI $DIC[\ILIAS\Language\Activities\AddLanguageEntry::class] = fn(): \ILIAS\Language\Activities\AddLanguageEntry => $this->add_language_entry; $DIC[\ILIAS\Language\Activities\SetLanguageDetectionEnabled::class] = fn(): \ILIAS\Language\Activities\SetLanguageDetectionEnabled => $this->set_language_detection_enabled; $DIC[\ILIAS\Language\Activities\SetLanguageTranslationEnabled::class] = fn(): \ILIAS\Language\Activities\SetLanguageTranslationEnabled => $this->set_language_translation_enabled; + $DIC['bt.registry'] = fn(): BackgroundTasksRegistry => $this->background_tasks_registry; } public function getName(): string From 88e89923143aa4a900eea1dce8d2f190862437be Mon Sep 17 00:00:00 2001 From: Fabian Schmid Date: Tue, 27 Oct 2026 17:57:31 +0100 Subject: [PATCH 13/14] FileServices: Contribute download jobs and keep their former names The download jobs receive their dependencies through the constructor and are contributed to BackgroundTasks under their new names. The former names stay as empty, deprecated subclasses, contributed as well, so buckets created before the update still load. This replaces the database step rewriting il_bt_task and il_bt_value. The former names will be removed with ILIAS 13. The global download limit is a setting of File, so FileServices defines DownloadLimit and File implements it with its General settings. The repository tree is not wired through the bootstrap yet; the jobs reach it through an internal RepositoryTreeProxy until it is. Checking access of the current user goes through Access::checkAccess(), so the jobs no longer need the user. CheckSumOfFileSizesJob no longer creates the unused ilSetting("fold"). --- components/ILIAS/File/File.php | 8 ++ .../ILIAS/File/classes/Settings/General.php | 4 +- .../ILIAS/FileServices/FileServices.php | 79 +++++++++++++++++++ .../class.ilCheckSumOfFileSizesJob.php | 29 +++++++ .../Download/class.ilCollectFilesJob.php | 29 +++++++ .../Download/class.ilCopyDefinition.php | 29 +++++++ .../class.ilCopyFilesToTempDirectoryJob.php | 29 +++++++ .../class.ilDownloadZipInteraction.php | 29 +++++++ ...ss.ilSumOfFileSizesTooLargeInteraction.php | 29 +++++++ .../classes/Download/class.ilZipJob.php | 29 +++++++ .../src/Download/CheckSumOfFileSizesJob.php | 37 +++------ .../src/Download/CollectFilesJob.php | 30 +++---- .../Download/CopyFilesToTempDirectoryJob.php | 13 +-- .../src/Download/DownloadLimit.php | 29 +++++++ .../src/Download/DownloadZipInteraction.php | 15 ++-- .../SumOfFileSizesTooLargeInteraction.php | 12 ++- .../FileServices/src/Download/ZipJob.php | 16 ++-- .../src/Legacy/RepositoryTreeProxy.php | 44 +++++++++++ 18 files changed, 410 insertions(+), 80 deletions(-) create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilCheckSumOfFileSizesJob.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilCollectFilesJob.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilCopyDefinition.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilCopyFilesToTempDirectoryJob.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilDownloadZipInteraction.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilSumOfFileSizesTooLargeInteraction.php create mode 100644 components/ILIAS/FileServices/classes/Download/class.ilZipJob.php create mode 100644 components/ILIAS/FileServices/src/Download/DownloadLimit.php create mode 100644 components/ILIAS/FileServices/src/Legacy/RepositoryTreeProxy.php diff --git a/components/ILIAS/File/File.php b/components/ILIAS/File/File.php index e04c9d967608..22a4bb58fc58 100644 --- a/components/ILIAS/File/File.php +++ b/components/ILIAS/File/File.php @@ -39,6 +39,9 @@ use ILIAS\File\Activities\ListFiles; use ILIAS\File\Activities\ReplaceFileContent; use ILIAS\File\Activities\RepositoryProvider; +use ILIAS\FileServices\Download\DownloadLimit; +use ILIAS\components\File\Settings\General; +use ILIAS\Database\Connection; use ILIAS\FileDelivery\Activities\FilePayloadFactory; use ILIAS\ResourceStorage\Activities\DeliverResource; use ILIAS\ResourceStorage\Activities\ServiceProvider as StorageProvider; @@ -61,6 +64,11 @@ public function init( array | \ArrayAccess &$pull, array | \ArrayAccess &$internal, ): void { + $implement[DownloadLimit::class] = static fn() => + new General( + $pull[Connection::class] + ); + $contribute[Agent::class] = static fn() => new \ilFileObjectAgent( $pull[Factory::class] diff --git a/components/ILIAS/File/classes/Settings/General.php b/components/ILIAS/File/classes/Settings/General.php index db8d04aac866..e7ec88aadd99 100755 --- a/components/ILIAS/File/classes/Settings/General.php +++ b/components/ILIAS/File/classes/Settings/General.php @@ -18,10 +18,12 @@ namespace ILIAS\components\File\Settings; +use ILIAS\FileServices\Download\DownloadLimit; + /** * @author Fabian Schmid */ -class General +class General implements DownloadLimit { public const MODULE_NAME = 'file_access'; public const F_BG_LIMIT = 'bg_limit'; diff --git a/components/ILIAS/FileServices/FileServices.php b/components/ILIAS/FileServices/FileServices.php index 5bf12a54f30d..8c2ce122b25c 100644 --- a/components/ILIAS/FileServices/FileServices.php +++ b/components/ILIAS/FileServices/FileServices.php @@ -33,6 +33,15 @@ use ILIAS\FileServices\Policy\UploadRestrictionBypassLegacyProxy; use ILIAS\Language\ComponentTranslation\LanguageFileDirectory; use ILIAS\Language\ComponentTranslation\ComponentLanguageFileDirectory; +use ILIAS\BackgroundTasks\Task; +use ILIAS\BackgroundTasks\Value; +use ILIAS\Filesystem\FileSystems\FilesystemTemp; +use ILIAS\Filesystem\Util\Archive\Archives; +use ILIAS\Language\Language; +use ILIAS\Logging\Logger\LoggerFactoryInterface; +use ILIAS\FileServices\Download; +use ILIAS\FileServices\Legacy\RepositoryTreeProxy; +use ILIAS\AccessControl\PublicInterface\Access; class FileServices implements Component { @@ -47,6 +56,7 @@ public function init( array|\ArrayAccess &$internal, ): void { $define[] = UploadRestrictionBypass::class; + $define[] = Download\DownloadLimit::class; $implement[UploadRestrictionBypass::class] = static fn() => new UploadRestrictionBypassLegacyProxy(); $implement[PhpUploadLimit::class] = static fn() => new FileServicesLegacyInitialisationAdapter( @@ -64,5 +74,74 @@ public function init( $this, 'fils' ); + + $contribute[Value::class] = static fn() => new Download\CopyDefinition(); + $contribute[Task::class] = static fn() => + new Download\CollectFilesJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[Access::class], + $internal[RepositoryTreeProxy::class] + ); + $contribute[Task::class] = static fn() => + new Download\CheckSumOfFileSizesJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[Access::class], + $internal[RepositoryTreeProxy::class], + $use[Download\DownloadLimit::class] + ); + $contribute[Task::class] = static fn() => + new Download\CopyFilesToTempDirectoryJob( + $use[LoggerFactoryInterface::class]->getLazy('cal') + ); + $contribute[Task::class] = static fn() => + new Download\ZipJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + new Archives() + ); + $contribute[Task::class] = static fn() => + new Download\DownloadZipInteraction( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[FilesystemTemp::class] + ); + $contribute[Task::class] = static fn() => + new Download\SumOfFileSizesTooLargeInteraction( + $use[Language::class] + ); + + // names of buckets created before ILIAS 12, remove with ILIAS 13 + $contribute[Value::class] = static fn() => new \ilCopyDefinition(); + $contribute[Task::class] = static fn() => + new \ilCollectFilesJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[Access::class], + $internal[RepositoryTreeProxy::class] + ); + $contribute[Task::class] = static fn() => + new \ilCheckSumOfFileSizesJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[Access::class], + $internal[RepositoryTreeProxy::class], + $use[Download\DownloadLimit::class] + ); + $contribute[Task::class] = static fn() => + new \ilCopyFilesToTempDirectoryJob( + $use[LoggerFactoryInterface::class]->getLazy('cal') + ); + $contribute[Task::class] = static fn() => + new \ilZipJob( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + new Archives() + ); + $contribute[Task::class] = static fn() => + new \ilDownloadZipInteraction( + $use[LoggerFactoryInterface::class]->getLazy('cal'), + $use[FilesystemTemp::class] + ); + $contribute[Task::class] = static fn() => + new \ilSumOfFileSizesTooLargeInteraction( + $use[Language::class] + ); + + $internal[RepositoryTreeProxy::class] = static fn() => new RepositoryTreeProxy(); } } diff --git a/components/ILIAS/FileServices/classes/Download/class.ilCheckSumOfFileSizesJob.php b/components/ILIAS/FileServices/classes/Download/class.ilCheckSumOfFileSizesJob.php new file mode 100644 index 000000000000..a30758dc3a33 --- /dev/null +++ b/components/ILIAS/FileServices/classes/Download/class.ilCheckSumOfFileSizesJob.php @@ -0,0 +1,29 @@ +logger = $DIC->logger()->cal(); - $this->settings = new \ilSetting("fold"); + public function __construct( + private readonly LoggerInterface $logger, + private readonly Access $access, + private readonly RepositoryTreeProxy $tree, + private readonly DownloadLimit $download_limit + ) { } @@ -84,11 +80,8 @@ public function run(array $input, Observer $observer): Value $definition = $input[0]; $object_ref_ids = $definition->getObjectRefIds(); - global $DIC; - // get global limit (max sum of individual file-sizes) from file settings - $general = new General($DIC->database()); - $size_limit = $general->getDownloadLimitinMB(); + $size_limit = $this->download_limit->getDownloadLimitinMB(); $size_limit_bytes = $size_limit * 1024 * 1024; $this->logger->debug('Global limit (max sum of all file-sizes) in file-settings: ' . $size_limit_bytes . ' bytes'); // get sum of individual file-sizes @@ -115,9 +108,6 @@ public function run(array $input, Observer $observer): Value */ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void { - global $DIC; - $tree = $DIC['tree']; - // parse folders foreach ($a_ref_ids as $ref_id) { if (!$this->validateAccess($ref_id)) { @@ -127,7 +117,7 @@ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void switch (\ilObject::_lookupType($ref_id, true)) { case "fold": // get child objects - $subtree = $tree->getChildsByTypeFilter($ref_id, ["fold", "file"]); + $subtree = $this->tree->getChildsByTypeFilter($ref_id, ["fold", "file"]); if (count($subtree) > 0) { $child_ref_ids = []; foreach ($subtree as $child) { @@ -152,10 +142,7 @@ protected function calculateRecursive(array $a_ref_ids, int &$a_file_size): void */ protected function validateAccess(int $ref_id): bool { - global $DIC; - $ilAccess = $DIC['ilAccess']; - - if (!$ilAccess->checkAccess("read", "", $ref_id)) { + if (!$this->access->checkAccess("read", "", $ref_id)) { return false; } return !\ilObject::_isInTrash($ref_id); diff --git a/components/ILIAS/FileServices/src/Download/CollectFilesJob.php b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php index 92fbbde06aa0..1699f9f1445a 100755 --- a/components/ILIAS/FileServices/src/Download/CollectFilesJob.php +++ b/components/ILIAS/FileServices/src/Download/CollectFilesJob.php @@ -24,24 +24,24 @@ use ILIAS\BackgroundTasks\Types\SingleType; use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; +use ILIAS\AccessControl\PublicInterface\Access; +use ILIAS\FileServices\Legacy\RepositoryTreeProxy; +use ILIAS\Logging\Logger\LoggerInterface; class CollectFilesJob extends AbstractJob { use \ilObjFileSecureString; - private ?\ilLogger $logger; /** * @var array */ private static array $duplicate_count_by_target = []; - /** - * Construct - */ - public function __construct() - { - global $DIC; - $this->logger = $DIC->logger()->cal(); + public function __construct( + private readonly LoggerInterface $logger, + private readonly Access $access, + private readonly RepositoryTreeProxy $tree + ) { } @@ -152,11 +152,7 @@ public function run(array $input, Observer $observer): Value */ private function getFileDirs($a_ref_id, string $a_file_name, string $a_temp_dir): false|array { - global $DIC; - - $user = $DIC->user(); - $ilAccess = $DIC->access(); - if ($ilAccess->checkAccessOfUser($user->getId(), "read", "", $a_ref_id)) { + if ($this->access->checkAccess("read", "", $a_ref_id)) { $file = new \ilObjFile($a_ref_id); $source_dir = $file->getFile(); @@ -209,11 +205,7 @@ private function getFileDirs($a_ref_id, string $a_file_name, string $a_temp_dir) */ private function recurseFolder($a_ref_id, string $a_folder_name, string $a_temp_dir, int|float $a_num_recursions, $a_initiated_by_folder_action): array { - global $DIC; - $num_recursions = $a_num_recursions + 1; - $tree = $DIC->repositoryTree(); - $ilAccess = $DIC->access(); $files = []; // Avoid the duplication of the uppermost folder when the download is initiated via a folder's action drop-down @@ -224,10 +216,10 @@ private function recurseFolder($a_ref_id, string $a_folder_name, string $a_temp_ $temp_dir = $a_temp_dir . '/' . \ilFileUtils::getASCIIFilename($a_folder_name); } - $subtree = $tree->getChildsByTypeFilter($a_ref_id, ["fold", "file"]); + $subtree = $this->tree->getChildsByTypeFilter($a_ref_id, ["fold", "file"]); foreach ($subtree as $child) { - if (!$ilAccess->checkAccess("read", "", $child["ref_id"])) { + if (!$this->access->checkAccess("read", "", $child["ref_id"])) { continue; } if (\ilObject::_isInTrash($child["ref_id"])) { diff --git a/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php index fb000cdaaed8..2f86570c81e0 100755 --- a/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php +++ b/components/ILIAS/FileServices/src/Download/CopyFilesToTempDirectoryJob.php @@ -24,20 +24,15 @@ use ILIAS\BackgroundTasks\Types\SingleType; use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; +use ILIAS\Logging\Logger\LoggerInterface; class CopyFilesToTempDirectoryJob extends AbstractJob { - private ?\ilLogger $logger; protected string $target_directory = ''; - - /** - * Constructor - */ - public function __construct() - { - global $DIC; - $this->logger = $DIC->logger()->cal(); + public function __construct( + private readonly LoggerInterface $logger + ) { } diff --git a/components/ILIAS/FileServices/src/Download/DownloadLimit.php b/components/ILIAS/FileServices/src/Download/DownloadLimit.php new file mode 100644 index 000000000000..255a0409ab17 --- /dev/null +++ b/components/ILIAS/FileServices/src/Download/DownloadLimit.php @@ -0,0 +1,29 @@ +logger = $DIC->logger()->cal(); + public function __construct( + private readonly LoggerInterface $logger, + private readonly FilesystemTemp $temp_filesystem + ) { } @@ -90,7 +90,6 @@ public function getOptions(array $input): array */ public function interaction(array $input, Option $user_selected_option, Bucket $bucket): Value { - global $DIC; $zip_name = $input[1]; $download_name = $input[0]; @@ -100,7 +99,7 @@ public function interaction(array $input, Option $user_selected_option, Bucket $ if ($user_selected_option->getValue() !== self::OPTION_DOWNLOAD) { $this->logger->info('Download canceled'); // delete zip file - $filesystem = $DIC->filesystem()->temp(); + $filesystem = $this->temp_filesystem; try { $path = LegacyPathHelper::createRelativePath($zip_name->getValue()); diff --git a/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php index a50f60b2d90d..33c692e9cb8c 100755 --- a/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php +++ b/components/ILIAS/FileServices/src/Download/SumOfFileSizesTooLargeInteraction.php @@ -25,6 +25,7 @@ use ILIAS\BackgroundTasks\Types\SingleType; use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; +use ILIAS\Language\Language; class SumOfFileSizesTooLargeInteraction extends AbstractUserInteraction { @@ -33,13 +34,9 @@ class SumOfFileSizesTooLargeInteraction extends AbstractUserInteraction */ private const OPTION_OK = 'ok'; - protected \ilLanguage $lng; - - public function __construct() - { - global $DIC; - $this->lng = $DIC->language(); - $this->lng->loadLanguageModule('background_tasks'); + public function __construct( + protected readonly Language $lng + ) { } /** @@ -84,6 +81,7 @@ public function getOptions(array $input): array #[\Override] public function getMessage(array $input): string { + $this->lng->loadLanguageModule('background_tasks'); return $this->lng->txt('ui_msg_files_violate_maxsize'); } diff --git a/components/ILIAS/FileServices/src/Download/ZipJob.php b/components/ILIAS/FileServices/src/Download/ZipJob.php index 079904a9bd13..905ed2b0e205 100755 --- a/components/ILIAS/FileServices/src/Download/ZipJob.php +++ b/components/ILIAS/FileServices/src/Download/ZipJob.php @@ -25,20 +25,14 @@ use ILIAS\BackgroundTasks\Types\SingleType; use ILIAS\BackgroundTasks\Types\Type; use ILIAS\BackgroundTasks\Value; +use ILIAS\Logging\Logger\LoggerInterface; class ZipJob extends AbstractJob { - private ?\ilLogger $logger; - private Archives $archives; - - /** - * Construct - */ - public function __construct() - { - global $DIC; - $this->logger = $DIC->logger()->cal(); - $this->archives = $DIC->archives(); + public function __construct( + private readonly LoggerInterface $logger, + private readonly Archives $archives + ) { } diff --git a/components/ILIAS/FileServices/src/Legacy/RepositoryTreeProxy.php b/components/ILIAS/FileServices/src/Legacy/RepositoryTreeProxy.php new file mode 100644 index 000000000000..d6135d640a85 --- /dev/null +++ b/components/ILIAS/FileServices/src/Legacy/RepositoryTreeProxy.php @@ -0,0 +1,44 @@ +> + */ + public function getChildsByTypeFilter(int $ref_id, array $types): array + { + return $this->tree()->getChildsByTypeFilter($ref_id, $types); + } + + private function tree(): \ilTree + { + global $DIC; + return $DIC->repositoryTree(); + } +} From 04758dfca86d2073468349bfc0f0072ff59d58d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Jou=C3=9Fen?= Date: Wed, 28 Oct 2026 10:35:48 +0100 Subject: [PATCH 14/14] BackgroundTasks/Init: Use FQCN As Registry Key The background task registry was stored in the DIC under the alias `bt.registry`. Registration, the fallback in `ilInitialisation`, and `BackgroundTaskServices::registry()` now use `ILIAS\BackgroundTasks\Registry\Registry` as the container key, the same way other modern services are registered. --- .../ILIAS/BackgroundTasks/src/BackgroundTaskServices.php | 2 +- components/ILIAS/Init/classes/class.ilInitialisation.php | 6 +++--- components/ILIAS/Init/src/AllModernComponents.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php b/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php index e59ad6e9f890..9de0104d6c57 100755 --- a/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php +++ b/components/ILIAS/BackgroundTasks/src/BackgroundTaskServices.php @@ -58,6 +58,6 @@ public function injector(): Injector public function registry(): Registry { - return $this->container['bt.registry']; + return $this->container[Registry::class]; } } diff --git a/components/ILIAS/Init/classes/class.ilInitialisation.php b/components/ILIAS/Init/classes/class.ilInitialisation.php index b55e80e73a16..a6c67d3d6704 100755 --- a/components/ILIAS/Init/classes/class.ilInitialisation.php +++ b/components/ILIAS/Init/classes/class.ilInitialisation.php @@ -1707,8 +1707,8 @@ private static function initBackgroundTasks(\ILIAS\DI\Container $c): void $sync = $sync ?: 'sync'; // The default value is sync. // populated by AllModernComponents, empty for scripts calling initILIAS() directly - if (!isset($c["bt.registry"])) { - $c["bt.registry"] = function ($c) { + if (!isset($c[\ILIAS\BackgroundTasks\Registry\Registry::class])) { + $c[\ILIAS\BackgroundTasks\Registry\Registry::class] = function ($c) { return new \ILIAS\BackgroundTasks\Implementation\Registry\BasicRegistry([], []); }; } @@ -1716,7 +1716,7 @@ private static function initBackgroundTasks(\ILIAS\DI\Container $c): void $c["bt.task_factory"] = function ($c) { return new \ILIAS\BackgroundTasks\Implementation\Tasks\BasicTaskFactory( $c["di.injector"], - $c["bt.registry"] + $c[\ILIAS\BackgroundTasks\Registry\Registry::class] ); }; diff --git a/components/ILIAS/Init/src/AllModernComponents.php b/components/ILIAS/Init/src/AllModernComponents.php index 62432f2e41b3..820d0a32ea5f 100644 --- a/components/ILIAS/Init/src/AllModernComponents.php +++ b/components/ILIAS/Init/src/AllModernComponents.php @@ -281,7 +281,7 @@ protected function populateComponentsInLegacyEnvironment(\ILIAS\DI\Container $DI $DIC[\ILIAS\Language\Activities\AddLanguageEntry::class] = fn(): \ILIAS\Language\Activities\AddLanguageEntry => $this->add_language_entry; $DIC[\ILIAS\Language\Activities\SetLanguageDetectionEnabled::class] = fn(): \ILIAS\Language\Activities\SetLanguageDetectionEnabled => $this->set_language_detection_enabled; $DIC[\ILIAS\Language\Activities\SetLanguageTranslationEnabled::class] = fn(): \ILIAS\Language\Activities\SetLanguageTranslationEnabled => $this->set_language_translation_enabled; - $DIC['bt.registry'] = fn(): BackgroundTasksRegistry => $this->background_tasks_registry; + $DIC[BackgroundTasksRegistry::class] = fn(): BackgroundTasksRegistry => $this->background_tasks_registry; } public function getName(): string