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 <[email protected]>
This commit is contained in:
frommeandClaude Opus 5.5 committed 2026-10-24 18:27:50 +02:00
1 parent f193d356cb
commit 520feb003f
5 files changed
+147 -1

No files matched your search

+1
View File
@@ -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",
Generated
+56 -1
View File
@@ -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": "[email protected]",
"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",
+3
View File
@@ -94,6 +94,9 @@ return [
'showCheckIntegrityInfoInReports' => '1',
'showCheckIntegrityInfoInReportsSeconds' => '86400',
],
'scheduler' => [
'maxLifetime' => '1440',
],
],
'FE' => [
'cacheHash' => [
@@ -0,0 +1,82 @@
<?php
namespace EP\EpProducts\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository;
use TYPO3\CMS\Scheduler\Task\ExecuteSchedulableCommandTask;
#[AsCommand(name: 'ep:scheduler-tasks', description: 'Replaces the TYPO3 10 import tasks with scheduler tasks running the ep:* commands')]
class SchedulerTasksCommand extends Command
{
/**
* TYPO3 10 tasks TYPO3 13 cannot run: 5/10 ImportSnowreportTask/ImportDatesTask (classes gone), 6 ImportContingentsTask
* (off since 2026-08), 16-21 one ep:groups-swiss task per house (API keys in the database), 25 garbage collection of
* the retired tx_epevents_log. The core housekeeping tasks 13, 14, 22, 23 and 24 work unchanged.
*/
private const OLD_TASKS = [5, 6, 10, 16, 17, 18, 19, 20, 21, 25];
/**
* Same schedules and groups as on TYPO3 10 (1 Datenimport, 2 Datenexport); ep:groups-swiss without arguments
* exports all houses of groupsSwissHouses in secrets.php.
*/
private const NEW_TASKS = [
['command' => '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;
}
}
+5
View File
@@ -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',