Files
cms-scheduler/Classes/Command/SchedulerExecuteCommand.php
T

174 lines
6.7 KiB
PHP

<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Scheduler\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Attribute\AsNonSchedulableCommand;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository;
use TYPO3\CMS\Scheduler\Scheduler;
use TYPO3\CMS\Scheduler\Service\TaskService;
/**
* CLI command for EXT:scheduler to execute tasks
*/
#[AsCommand('scheduler:execute', 'Execute given TYPO3 Scheduler tasks.')]
#[AsNonSchedulableCommand]
class SchedulerExecuteCommand extends Command
{
protected SymfonyStyle $io;
public function __construct(
protected readonly Context $context,
protected readonly SchedulerTaskRepository $taskRepository,
protected readonly TaskService $taskService,
protected Scheduler $scheduler,
) {
parent::__construct();
}
protected function configure(): void
{
$this
->addOption(
'task',
't',
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Execute tasks by given id. To run all tasks of a group prefix the group id with "g:", e.g. "g:1"',
);
}
public function execute(InputInterface $input, OutputInterface $output): int
{
// Make sure the _cli_ user is loaded
Bootstrap::initializeBackendAuthentication();
$this->io = new SymfonyStyle($input, $output);
if (count($input->getOption('task')) > 0) {
$taskGroups = $this->taskRepository->getGroupedTasks()['taskGroupsWithTasks'];
$tasksToRun = $this->getTasksToRun($taskGroups, $input->getOption('task'));
$this->runTasks($tasksToRun, $taskGroups);
return Command::SUCCESS;
}
$this->askForTasksAndRun($input, $output);
return Command::SUCCESS;
}
private function askForTasksAndRun(InputInterface $input, OutputInterface $output): void
{
/** @var QuestionHelper $questionHelper */
$questionHelper = $this->getHelper('question');
$taskGroups = $this->taskRepository->getGroupedTasks()['taskGroupsWithTasks'];
$selectableTasks = $this->getSelectableTasks($taskGroups);
if ($selectableTasks === []) {
$this->io->note('No tasks available.');
return;
}
$tasksToRunQuestion = new ChoiceQuestion('Run tasks (comma-separated list): ', $selectableTasks);
$tasksToRunQuestion->setAutocompleterValues(array_keys($selectableTasks));
$tasksToRunQuestion->setMultiselect(true);
$tasksToRun = $questionHelper->ask($input, $output, $tasksToRunQuestion);
$this->runTasks($tasksToRun, $taskGroups);
}
private function runTasks($selectedTasks, $taskGroups): void
{
$taskUids = $this->getTaskUidsFromSelection($selectedTasks, $taskGroups);
ksort($taskUids);
$numLength = strlen((string)array_reverse($taskUids)[0]);
foreach ($taskUids as $taskUid) {
try {
$uid = (int)$taskUid;
$task = $this->taskRepository->findByUid($uid);
$additionalInformation = $task->getAdditionalInformation() === '' ? '' : ' (' . $task->getAdditionalInformation() . ')';
$taskDetails = $this->taskService->getTaskDetailsFromTask($task);
$space = str_repeat(' ', $numLength - strlen((string)$task->getTaskUid()));
$this->io->writeln('[ <fg=green>TASK:' . $task->getTaskUid() . $space . '</> ] Running "' . $taskDetails['title'] . $additionalInformation . '"');
$this->scheduler->executeTask($task);
} catch (\Throwable $exception) {
$this->io->writeln($exception->getMessage());
}
}
}
private function getTaskUidsFromSelection(array $list, array $groups): array
{
$taskUids = [];
foreach ($list as $uid) {
[$keyword, $group] = [...explode(':', (string)$uid), null];
if ($keyword === 'g') {
if (!array_key_exists($group, $groups)) {
throw new \InvalidArgumentException('Group with id "' . $group . '" does not exist.', 1679683415);
}
$taskUidsInGroup = array_column($groups[$group]['tasks'], 'uid');
$taskUids += $taskUidsInGroup;
} else {
$taskUids[] = $uid;
}
}
return $taskUids;
}
private function getLanguageService(): LanguageService
{
return GeneralUtility::makeInstance(LanguageServiceFactory::class)->create('en');
}
private function getTasksToRun(array $taskGroups, array $taskList): array
{
$taskUids = array_unique($this->getTaskUidsFromSelection($taskList, $taskGroups));
foreach ($taskUids as $taskUid) {
// This will throw an exception if the task uid was not found and print it to the console.
$this->taskRepository->findByUid((int)$taskUid);
}
return $taskUids;
}
protected function getSelectableTasks(mixed $taskGroups): array
{
$selectableTasks = [];
foreach ($taskGroups as $uid => $group) {
$groupLabel = ($group['groupName'] ?? $this->getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.noGroup'));
$selectableTasks['g:' . $uid] = '<fg=yellow>' . $groupLabel . '</>';
foreach ($group['tasks'] as $task) {
$additionalInformation = $task['additionalInformation'] === '' ? '' : ' (' . $task['additionalInformation'] . ')';
$selectableTasks[$task['uid']] = $task['fullTitle'] . $additionalInformation;
}
}
return $selectableTasks;
}
}