From 520feb003fd6317d25328c07ece7616c98ac1dd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Fromme?= Date: Sat, 24 Oct 2026 18:27:50 +0200 Subject: [PATCH] feat: scheduler runs the imports and the housekeeping tasks again typo3/cms-scheduler 13.4 replaces the three direct cron jobs with one scheduler:run cron job. ep:scheduler-tasks (one-off, idempotent) soft-deletes the TYPO3 10 tasks TYPO3 13 cannot run (import task classes that are gone, one groups.swiss task per house with the API keys in the database, the retired E&P Events log) and adds "Execute console commands" tasks for ep:import-dates, ep:import-snowreport and ep:groups-swiss with the TYPO3 10 schedules. The core garbage collection, IP anonymization and file indexing tasks keep working as they are; tx_epproducts_log is registered for garbage collection again. The first execution is the next cron match, so adding the tasks never starts an import or a groups.swiss export right away. Co-Authored-By: Claude Opus 5.5 --- composer.json | 1 + composer.lock | 57 ++++++++++++- config/system/settings.php | 3 + .../Classes/Command/SchedulerTasksCommand.php | 82 +++++++++++++++++++ packages/ep_products/ext_localconf.php | 5 ++ 5 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 packages/ep_products/Classes/Command/SchedulerTasksCommand.php diff --git a/composer.json b/composer.json index d576f7d..1ad60aa 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "typo3/cms-recycler": "^13.4", "typo3/cms-redirects": "^13.4", "typo3/cms-rte-ckeditor": "^13.4", + "typo3/cms-scheduler": "^13.4", "typo3/cms-seo": "^13.4", "typo3/cms-setup": "^13.4", "typo3/cms-sys-note": "^13.4", diff --git a/composer.lock b/composer.lock index 12a15df..edb338a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "440f3e580365f1608cf72549ef769104", + "content-hash": "85a0f79761fbcaf517b130c2f7a3e114", "packages": [ { "name": "b13/container", @@ -8180,6 +8180,61 @@ }, "time": "2026-10-09T09:26:19+00:00" }, + { + "name": "typo3/cms-scheduler", + "version": "v13.4.36", + "source": { + "type": "git", + "url": "https://github.com/TYPO3-CMS/scheduler.git", + "reference": "5e2d1cddedbf479d17eb5f6768fa006895c194e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/5e2d1cddedbf479d17eb5f6768fa006895c194e9", + "reference": "5e2d1cddedbf479d17eb5f6768fa006895c194e9", + "shasum": "" + }, + "require": { + "typo3/cms-core": "13.4.36" + }, + "conflict": { + "typo3/cms": "*" + }, + "type": "typo3-cms-framework", + "extra": { + "typo3/cms": { + "extension-key": "scheduler" + }, + "branch-alias": { + "dev-main": "13.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Scheduler\\": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "TYPO3 Core Team", + "email": "typo3cms@typo3.org", + "role": "Developer" + } + ], + "description": "TYPO3 CMS Scheduler - Schedule tasks to run once or periodically at a specific time.", + "homepage": "https://typo3.org", + "support": { + "chat": "https://typo3.org/help", + "docs": "https://docs.typo3.org/c/typo3/cms-scheduler/main/en-us", + "issues": "https://forge.typo3.org", + "source": "https://github.com/typo3/typo3" + }, + "time": "2026-10-09T09:26:19+00:00" + }, { "name": "typo3/cms-seo", "version": "v13.4.36", diff --git a/config/system/settings.php b/config/system/settings.php index 7c41514..d7c0b03 100644 --- a/config/system/settings.php +++ b/config/system/settings.php @@ -94,6 +94,9 @@ return [ 'showCheckIntegrityInfoInReports' => '1', 'showCheckIntegrityInfoInReportsSeconds' => '86400', ], + 'scheduler' => [ + 'maxLifetime' => '1440', + ], ], 'FE' => [ 'cacheHash' => [ diff --git a/packages/ep_products/Classes/Command/SchedulerTasksCommand.php b/packages/ep_products/Classes/Command/SchedulerTasksCommand.php new file mode 100644 index 0000000..9d28ca2 --- /dev/null +++ b/packages/ep_products/Classes/Command/SchedulerTasksCommand.php @@ -0,0 +1,82 @@ + 'ep:import-dates', 'cron' => '*/15 6-20 * * *', 'multiple' => true, 'group' => 1, 'description' => 'Reisedaten aus MyE&P importieren'], + ['command' => 'ep:import-snowreport', 'cron' => '0 6 * * *', 'multiple' => false, 'group' => 1, 'description' => 'Schneehöhen importieren'], + ['command' => 'ep:groups-swiss', 'cron' => '0 14 * * *', 'multiple' => false, 'group' => 2, 'description' => 'Belegung der Gruppenhäuser nach groups.swiss exportieren'], + ]; + + public function __construct( + private readonly SchedulerTaskRepository $taskRepository, + private readonly ConnectionPool $connectionPool, + ) { + parent::__construct(); + } + + protected function configure(): void + { + $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Only show what would change'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $dryRun = (bool)$input->getOption('dry-run'); + + $existing = []; + foreach ($this->connectionPool->getConnectionForTable('tx_scheduler_task')->select(['uid', 'serialized_task_object'], 'tx_scheduler_task', ['deleted' => 0])->fetchAllAssociative() as $row) { + if (in_array((int)$row['uid'], self::OLD_TASKS, true)) { + $output->writeln(sprintf('Delete TYPO3 10 task %d', $row['uid'])); + if (!$dryRun) { + $this->taskRepository->remove((int)$row['uid']); + } + } elseif (preg_match('/s:20:"\0\*\0commandIdentifier";s:\d+:"([^"]+)"/', (string)$row['serialized_task_object'], $matches)) { + $existing[$matches[1]] = (int)$row['uid']; + } + } + + foreach (self::NEW_TASKS as $config) { + if (isset($existing[$config['command']])) { + $output->writeln(sprintf('Task for %s exists (%d)', $config['command'], $existing[$config['command']])); + continue; + } + $output->writeln(sprintf('Add task for %s (%s)', $config['command'], $config['cron'])); + if ($dryRun) { + continue; + } + $task = GeneralUtility::makeInstance(ExecuteSchedulableCommandTask::class); + $task->setCommandIdentifier($config['command']); + $task->setDescription($config['description']); + $task->setTaskGroup($config['group']); + $task->registerRecurringExecution($GLOBALS['EXEC_TIME'] - 1, 0, 0, $config['multiple'], $config['cron']); + $this->taskRepository->add($task); + } + + return Command::SUCCESS; + } +} diff --git a/packages/ep_products/ext_localconf.php b/packages/ep_products/ext_localconf.php index 9044c2f..8f0b72f 100644 --- a/packages/ep_products/ext_localconf.php +++ b/packages/ep_products/ext_localconf.php @@ -41,6 +41,11 @@ $boot = function () { // Client-side callers pass these parameters in POST bodies, which cHash never inspects. $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['requireCacheHashPresenceParameters'][] = '^tx_epproducts_ajax'; + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask::class]['options']['tables']['tx_epproducts_log'] = [ + 'dateField' => 'time_micro', + 'expirePeriod' => 180, + ]; + ExtensionUtility::configurePlugin( 'EpProducts', 'product_detail',