TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:33 +02:00
commit 1a6eae9988
124 changed files with 11393 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
<?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\Redirects\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Registry;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
use TYPO3\CMS\Redirects\Service\IntegrityService;
use TYPO3\CMS\Redirects\Utility\RedirectConflict;
#[AsCommand('redirects:checkintegrity', 'Check integrity of redirects')]
class CheckIntegrityCommand extends Command
{
private const string REGISTRY_NAMESPACE = 'tx_redirects';
public const REGISTRY_KEY_CONFLICTING_REDIRECTS = 'conflicting_redirects';
public const REGISTRY_KEY_LAST_TIMESTAMP_CHECK_INTEGRITY = 'redirects_check_integrity_last_check';
private const string LANGUAGE_FILE_PATH = 'LLL:EXT:redirects/Resources/Private/Language/locallang_db.xlf';
public function __construct(
private readonly Registry $registry,
private readonly IntegrityService $integrityService,
private readonly SiteFinder $siteFinder,
) {
parent::__construct();
}
protected function configure(): void
{
$this->addArgument(
'site',
InputArgument::OPTIONAL,
'If set, then only pages of a specific site are checked',
'',
function (): array {
return array_keys($this->siteFinder->getAllSites());
}
);
}
/**
* Executes the command for checking for conflicting redirects
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->registry->remove(self::REGISTRY_NAMESPACE, self::REGISTRY_KEY_CONFLICTING_REDIRECTS);
$this->registry->remove(self::REGISTRY_NAMESPACE, self::REGISTRY_KEY_LAST_TIMESTAMP_CHECK_INTEGRITY);
$conflictingRedirects = [];
$list = [];
$site = $input->getArgument('site') ?: null;
$table = new Table($output);
$table->setHeaders(
[
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . ':sys_redirect.uid'
),
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . ':sys_redirect.source_host'
),
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . ':sys_redirect.source_path'
),
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . ':sys_redirect.target'
),
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . ':sys_redirect.integrity_status'
),
]
);
$integrityStatusLabel = ':sys_redirect.integrity_status.';
foreach ($this->integrityService->findConflictingRedirects($site) as $conflict) {
$conflictingRedirects[] = [
$conflict['redirect']['uid'],
$conflict['redirect']['source_host'],
$conflict['redirect']['source_path'],
$conflict['uri'],
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . $integrityStatusLabel . $conflict['redirect']['integrity_status']
),
];
$list[] = $conflict;
$this->integrityService->setIntegrityStatus($conflict['redirect']);
}
foreach ($this->integrityService->checkRedirectIntegrity() as $conflict) {
if ($conflict['redirect']['integrity_status'] !== RedirectConflict::NO_CONFLICT) {
// Report only redirects with conflict status checked as conflicting redirects.
$conflictingRedirects[] = [
$conflict['redirect']['uid'],
$conflict['redirect']['source_host'],
$conflict['redirect']['source_path'],
$conflict['uri'],
LocalizationUtility::translate(
self::LANGUAGE_FILE_PATH . $integrityStatusLabel . $conflict['redirect']['integrity_status']
) ?? $conflict['redirect']['integrity_status'],
];
$list[] = $conflict;
}
// Always update redirect status
$this->integrityService->setIntegrityStatus($conflict['redirect']);
}
if ($conflictingRedirects !== []) {
$table->setRows($conflictingRedirects);
$table->render();
}
$this->registry->set(self::REGISTRY_NAMESPACE, self::REGISTRY_KEY_CONFLICTING_REDIRECTS, $list);
$this->registry->set(self::REGISTRY_NAMESPACE, self::REGISTRY_KEY_LAST_TIMESTAMP_CHECK_INTEGRITY, time());
return Command::SUCCESS;
}
}
+125
View File
@@ -0,0 +1,125 @@
<?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\Redirects\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\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Redirects\Repository\Demand;
use TYPO3\CMS\Redirects\Repository\RedirectRepository;
#[AsCommand('redirects:cleanup', 'Cleanup old redirects periodically for given constraints like days, hit count or domains.')]
class CleanupRedirectsCommand extends Command
{
protected LanguageService $languageService;
public function __construct(
protected readonly RedirectRepository $redirectRepository,
protected readonly LanguageServiceFactory $languageServiceFactory
) {
$this->languageService = $languageServiceFactory->create('en');
parent::__construct();
}
protected function configure(): void
{
$this
->addOption(
'domain',
'd',
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.domain'),
null,
function (): array {
return array_column($this->redirectRepository->findHostsOfRedirects(), 'name');
}
)
->addOption(
'statusCode',
's',
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.statusCode'),
null,
function (): array {
return array_column($this->redirectRepository->findStatusCodesOfRedirects(), 'code');
}
)
->addOption(
'days',
'a',
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.days'),
null
)
->addOption(
'hitCount',
'c',
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.hitCount'),
null
)
->addOption(
'path',
'p',
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.path'),
null
)
->addOption(
'creationType',
't',
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.creationType'),
null,
function (): array {
return array_keys($this->redirectRepository->findCreationTypes());
}
)
->addOption(
'integrityStatus',
'i',
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.integrityStatus'),
null,
function (): array {
return array_keys($this->redirectRepository->findIntegrityStatusCodes());
}
)
->addOption(
'redirectType',
null,
InputOption::VALUE_OPTIONAL,
$this->languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang.xlf:cleanupRedirectsCommand.label.redirectType'),
Demand::DEFAULT_REDIRECT_TYPE,
function (): array {
return array_keys($this->redirectRepository->findRedirectTypes());
}
)
;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$this->redirectRepository->removeByDemand(Demand::fromCommandInput($input));
return Command::SUCCESS;
}
}