TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Attribute\AsController;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentFactory;
|
||||
use TYPO3\CMS\Backend\Template\Components\MultiRecordSelection\Action;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Configuration\Features;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Redirects\Event\ModifyRedirectManagementControllerViewDataEvent;
|
||||
use TYPO3\CMS\Redirects\Repository\Demand;
|
||||
use TYPO3\CMS\Redirects\Repository\RedirectRepository;
|
||||
use TYPO3\CMS\Redirects\Service\ModulePaginationService;
|
||||
use TYPO3\CMS\Redirects\Utility\RedirectConflict;
|
||||
|
||||
/**
|
||||
* Lists all redirects in the TYPO3 Backend as a module.
|
||||
*
|
||||
* @internal This class is a specific TYPO3 Backend controller implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
#[AsController]
|
||||
class ManagementController
|
||||
{
|
||||
public function __construct(
|
||||
protected UriBuilder $uriBuilder,
|
||||
protected IconFactory $iconFactory,
|
||||
protected RedirectRepository $redirectRepository,
|
||||
protected ModuleTemplateFactory $moduleTemplateFactory,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
protected ComponentFactory $componentFactory,
|
||||
protected ModulePaginationService $modulePaginationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request, and renders the overview of all redirects
|
||||
*/
|
||||
public function handleRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->moduleTemplateFactory->create($request);
|
||||
$demand = Demand::fromRequest($request);
|
||||
$redirectType = $demand->getRedirectType();
|
||||
|
||||
$view->setTitle(
|
||||
$this->getLanguageService()->translate('title', 'redirects.modules.redirects')
|
||||
);
|
||||
$view->makeDocHeaderModuleMenu();
|
||||
$this->registerDocHeaderButtons($view);
|
||||
|
||||
if (!$this->canListRedirects()) {
|
||||
return $view->renderResponse('Management/Overview');
|
||||
}
|
||||
|
||||
$event = $this->eventDispatcher->dispatch(
|
||||
new ModifyRedirectManagementControllerViewDataEvent(
|
||||
$demand,
|
||||
$this->redirectRepository->findRedirectsByDemand($demand),
|
||||
$this->redirectRepository->findHostsOfRedirects($redirectType),
|
||||
$this->redirectRepository->findStatusCodesOfRedirects($redirectType),
|
||||
$this->redirectRepository->findCreationTypes($redirectType),
|
||||
GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('redirects.hitCount'),
|
||||
$view,
|
||||
$request,
|
||||
$this->redirectRepository->findIntegrityStatusCodes($redirectType),
|
||||
)
|
||||
);
|
||||
$requestUri = $request->getAttribute('normalizedParams')->getRequestUri();
|
||||
$pagination = $this->modulePaginationService->preparePagination($demand);
|
||||
$languageService = $this->getLanguageService();
|
||||
$view = $event->getView();
|
||||
$hasEditPermissions = $this->canEditRedirects();
|
||||
$view->assignMultiple([
|
||||
'redirects' => $event->getRedirects(),
|
||||
'hosts' => $event->getHosts(),
|
||||
'statusCodes' => $event->getStatusCodes(),
|
||||
'creationTypes' => $event->getCreationTypes(),
|
||||
'integrityStatusCodes' => $event->getIntegrityStatusCodes(),
|
||||
'defaultIntegrityStatus' => RedirectConflict::NO_CONFLICT,
|
||||
'demand' => $event->getDemand(),
|
||||
'showHitCounter' => $event->getShowHitCounter(),
|
||||
'pagination' => $pagination,
|
||||
'canEditRedirects' => $hasEditPermissions,
|
||||
'canListRedirects' => true,
|
||||
'returnUrl' => $this->uriBuilder->buildUriFromRoute('redirects', [
|
||||
'page' => $pagination['current'],
|
||||
'demand' => $demand->getParameters(),
|
||||
'orderField' => $demand->getOrderField(),
|
||||
'orderDirection' => $demand->getOrderDirection(),
|
||||
]),
|
||||
'actions' => $hasEditPermissions ? [
|
||||
new Action(
|
||||
'edit',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-open',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit'
|
||||
),
|
||||
new Action(
|
||||
'delete',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'title' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_module_redirect.xlf:labels.delete.title'),
|
||||
'content' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_module_redirect.xlf:labels.delete.message'),
|
||||
'ok' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'),
|
||||
'cancel' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel'),
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-edit-delete',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'
|
||||
),
|
||||
] : [],
|
||||
]);
|
||||
return $view->renderResponse('Management/Overview');
|
||||
}
|
||||
|
||||
protected function canListRedirects(): bool
|
||||
{
|
||||
return $this->getBackendUser()->check('tables_select', 'sys_redirect');
|
||||
}
|
||||
|
||||
protected function canEditRedirects(): bool
|
||||
{
|
||||
return $this->getBackendUser()->check('tables_modify', 'sys_redirect');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create document header buttons
|
||||
*/
|
||||
protected function registerDocHeaderButtons(ModuleTemplate $view): void
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
|
||||
// Create new
|
||||
if ($this->canEditRedirects()) {
|
||||
$newRecordButton = $this->componentFactory->createLinkButton()
|
||||
->setHref((string)$this->uriBuilder->buildUriFromRoute(
|
||||
'record_edit',
|
||||
[
|
||||
'edit' => ['sys_redirect' => ['new']],
|
||||
'module' => 'redirects',
|
||||
'defVals' => [
|
||||
'sys_redirect' => [
|
||||
'redirect_type' => Demand::DEFAULT_REDIRECT_TYPE,
|
||||
],
|
||||
],
|
||||
'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('redirects'),
|
||||
]
|
||||
))
|
||||
->setTitle($languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_module_redirect.xlf:redirect_add_text'))
|
||||
->setShowLabelText(true)
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL));
|
||||
$view->getDocHeaderComponent()->getButtonBar()->addButton($newRecordButton);
|
||||
}
|
||||
|
||||
// Shortcut
|
||||
$view->getDocHeaderComponent()->setShortcutContext(
|
||||
'redirects',
|
||||
$languageService->translate('short_description', 'redirects.modules.redirects')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Attribute\AsController;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentFactory;
|
||||
use TYPO3\CMS\Backend\Template\Components\MultiRecordSelection\Action;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
|
||||
use TYPO3\CMS\Core\Configuration\Features;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Redirects\Repository\Demand;
|
||||
use TYPO3\CMS\Redirects\Repository\RedirectRepository;
|
||||
use TYPO3\CMS\Redirects\Service\ModulePaginationService;
|
||||
use TYPO3\CMS\Redirects\Utility\RedirectConflict;
|
||||
|
||||
/**
|
||||
* Lists all QR Codes in the TYPO3 Backend as a module.
|
||||
*
|
||||
* @internal This class is a specific TYPO3 Backend controller implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
#[AsController]
|
||||
class QrCodeModuleController
|
||||
{
|
||||
public function __construct(
|
||||
protected UriBuilder $uriBuilder,
|
||||
protected IconFactory $iconFactory,
|
||||
protected RedirectRepository $redirectRepository,
|
||||
protected ModuleTemplateFactory $moduleTemplateFactory,
|
||||
protected ComponentFactory $componentFactory,
|
||||
protected ModulePaginationService $modulePaginationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request, and renders the overview of all QR Codes
|
||||
*/
|
||||
public function handleRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->moduleTemplateFactory->create($request);
|
||||
$demand = Demand::fromRequest($request);
|
||||
$redirectType = $demand->getRedirectType();
|
||||
|
||||
$view->setTitle(
|
||||
$this->getLanguageService()->translate('title', 'redirects.modules.qrcodes')
|
||||
);
|
||||
|
||||
$view->makeDocHeaderModuleMenu();
|
||||
$this->registerDocHeaderButtons($view);
|
||||
|
||||
$requestUri = $request->getAttribute('normalizedParams')->getRequestUri();
|
||||
$languageService = $this->getLanguageService();
|
||||
$pagination = $this->modulePaginationService->preparePagination($demand);
|
||||
$view->assignMultiple([
|
||||
'redirects' => $this->redirectRepository->findRedirectsByDemand($demand),
|
||||
'hosts' => $this->redirectRepository->findHostsOfRedirects($redirectType),
|
||||
'defaultIntegrityStatus' => RedirectConflict::NO_CONFLICT,
|
||||
'demand' => $demand,
|
||||
'showHitCounter' => GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('redirects.hitCount'),
|
||||
'pagination' => $pagination,
|
||||
'returnUrl' => $this->uriBuilder->buildUriFromRoute('qrcodes', [
|
||||
'page' => $pagination['current'],
|
||||
'demand' => $demand->getParameters(),
|
||||
'orderField' => $demand->getOrderField(),
|
||||
'orderDirection' => $demand->getOrderDirection(),
|
||||
]),
|
||||
'actions' => [
|
||||
new Action(
|
||||
'edit',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-open',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit'
|
||||
),
|
||||
new Action(
|
||||
'delete',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'title' => $languageService->translate('delete.title', 'redirects.modules.qrcodes'),
|
||||
'content' => $languageService->translate('delete.message', 'redirects.modules.qrcodes'),
|
||||
'ok' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'),
|
||||
'cancel' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel'),
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-edit-delete',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'
|
||||
),
|
||||
],
|
||||
]);
|
||||
return $view->renderResponse('QrCode/Overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create document header buttons for QR codes
|
||||
*/
|
||||
protected function registerDocHeaderButtons(ModuleTemplate $view): void
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
|
||||
// Create new
|
||||
$newRecordButton = $this->componentFactory->createLinkButton()
|
||||
->setHref((string)$this->uriBuilder->buildUriFromRoute(
|
||||
'record_edit',
|
||||
[
|
||||
'edit' => ['sys_redirect' => ['new']],
|
||||
'module' => 'qrcodes',
|
||||
'defVals' => [
|
||||
'sys_redirect' => [
|
||||
'redirect_type' => Demand::QRCODE_REDIRECT_TYPE,
|
||||
],
|
||||
],
|
||||
'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('qrcodes'),
|
||||
]
|
||||
))
|
||||
->setTitle($languageService->translate('add_text', 'redirects.modules.qrcodes'))
|
||||
->setShowLabelText(true)
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL));
|
||||
$view->addButtonToButtonBar($newRecordButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
|
||||
|
||||
$view->getDocHeaderComponent()->setShortcutContext(
|
||||
'qrcodes',
|
||||
$languageService->translate('short_description', 'redirects.modules.qrcodes')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Attribute\AsController;
|
||||
use TYPO3\CMS\Backend\History\RecordHistory;
|
||||
use TYPO3\CMS\Backend\History\RecordHistoryRollback;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\DataHandling\Model\CorrelationId;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Redirects\Service\TemporaryPermissionMutationService;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
#[AsController]
|
||||
readonly class RecordHistoryRollbackController
|
||||
{
|
||||
public function __construct(
|
||||
private LanguageServiceFactory $languageServiceFactory,
|
||||
private RecordHistoryRollback $recordHistoryRollback,
|
||||
private TemporaryPermissionMutationService $temporaryPermissionMutationService
|
||||
) {}
|
||||
|
||||
public function revertCorrelation(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$languageService = $this->languageServiceFactory->createFromUserPreferences($this->getBackendUser());
|
||||
$revertedCorrelationTypes = [];
|
||||
$correlationIds = $request->getParsedBody()['correlation_ids'] ?? [];
|
||||
/** @var CorrelationId[] $correlationIds */
|
||||
$correlationIds = array_map(
|
||||
static function (string $correlationId) {
|
||||
return CorrelationId::fromString($correlationId);
|
||||
},
|
||||
$correlationIds
|
||||
);
|
||||
foreach ($correlationIds as $correlationId) {
|
||||
$type = $correlationId->getAspects()[1] ?? null;
|
||||
if ($type !== null) {
|
||||
$revertedCorrelationTypes[] = $type;
|
||||
}
|
||||
$this->rollBackCorrelation($correlationId);
|
||||
}
|
||||
$result = [
|
||||
'status' => 'error',
|
||||
'title' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:redirects_error_title'),
|
||||
'message' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:redirects_error_message'),
|
||||
];
|
||||
if (in_array('redirect', $revertedCorrelationTypes, true)) {
|
||||
$result = [
|
||||
'status' => 'ok',
|
||||
'title' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:revert_redirects_success_title'),
|
||||
'message' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:revert_redirects_success_message'),
|
||||
];
|
||||
if (in_array('slug', $revertedCorrelationTypes, true)) {
|
||||
$result = [
|
||||
'status' => 'ok',
|
||||
'title' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:revert_update_success_title'),
|
||||
'message' => $languageService->sL('LLL:EXT:redirects/Resources/Private/Language/locallang_slug_service.xlf:revert_update_success_message'),
|
||||
];
|
||||
}
|
||||
}
|
||||
return new JsonResponse($result);
|
||||
}
|
||||
|
||||
protected function rollBackCorrelation(CorrelationId $correlationId): void
|
||||
{
|
||||
$currentUserId = $this->getBackendUser()->getUserId();
|
||||
$historyEntries = GeneralUtility::makeInstance(RecordHistory::class)->findEventsForCorrelation((string)$correlationId);
|
||||
|
||||
// Verify the correlation belongs to the current user before allowing rollback.
|
||||
// All entries sharing a correlation_id are written by the same user, so checking
|
||||
// any one entry is sufficient; we use the first (most recent) for the guard.
|
||||
$firstEntry = reset($historyEntries);
|
||||
if ($firstEntry === false || (int)$firstEntry['userid'] !== $currentUserId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Temporary add permissions to the user to perform the action.
|
||||
// Store if we need to revert those changes after the actions.
|
||||
$addedTableSelect = $this->temporaryPermissionMutationService->addTableSelect();
|
||||
$addedTableModify = $this->temporaryPermissionMutationService->addTableModify();
|
||||
|
||||
foreach ($historyEntries as $recordHistoryEntry) {
|
||||
$element = $recordHistoryEntry['tablename'] . ':' . $recordHistoryEntry['recuid'];
|
||||
$tempRecordHistory = GeneralUtility::makeInstance(RecordHistory::class, $element);
|
||||
$tempRecordHistory->setLastHistoryEntryNumber((int)$recordHistoryEntry['uid']);
|
||||
$this->recordHistoryRollback->performRollback('ALL', $tempRecordHistory->getDiff($tempRecordHistory->getChangeLog()));
|
||||
}
|
||||
|
||||
// Revert temporary permissions
|
||||
if ($addedTableSelect) {
|
||||
$this->temporaryPermissionMutationService->removeTableSelect();
|
||||
}
|
||||
if ($addedTableModify) {
|
||||
$this->temporaryPermissionMutationService->removeTableModify();
|
||||
}
|
||||
}
|
||||
|
||||
private function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Redirects\Service\ShortUrlService;
|
||||
|
||||
/**
|
||||
* @internal Only to be used within TYPO3. Might change in the future.
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
readonly class ShortUrlGeneratorController
|
||||
{
|
||||
public function __construct(
|
||||
protected ShortUrlService $shortUrlService,
|
||||
protected ResponseFactoryInterface $responseFactory,
|
||||
protected StreamFactoryInterface $streamFactory,
|
||||
) {}
|
||||
|
||||
public function generate(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$sourceHost = (string)($parsedBody['source_host'] ?? '');
|
||||
|
||||
$shortUrl = $this->shortUrlService->generateUniqueShortUrlPath($sourceHost);
|
||||
if ($shortUrl === null) {
|
||||
return $this->createResponse([
|
||||
'success' => false,
|
||||
'message' => 'Could not generate a unique short URL after multiple attempts.',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->createResponse([
|
||||
'success' => true,
|
||||
'shortUrl' => $shortUrl,
|
||||
]);
|
||||
}
|
||||
|
||||
public function validate(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$sourceHost = (string)($parsedBody['source_host'] ?? '');
|
||||
$sourcePath = (string)($parsedBody['source_path'] ?? '');
|
||||
|
||||
if ($sourcePath === '') {
|
||||
return $this->createResponse(['isUnique' => true]);
|
||||
}
|
||||
|
||||
// Ensure leading slash
|
||||
if ($sourcePath[0] !== '/') {
|
||||
$sourcePath = '/' . $sourcePath;
|
||||
}
|
||||
|
||||
$isUnique = $this->shortUrlService->isUniqueShortUrl($sourceHost, $sourcePath);
|
||||
$response = ['isUnique' => $isUnique];
|
||||
if (!$isUnique) {
|
||||
$response['message'] = $this->getLanguageService()
|
||||
->sL('redirects.modules.short_urls:validation.duplicate_short_url');
|
||||
}
|
||||
return $this->createResponse($response);
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
|
||||
private function createResponse(array $data): ResponseInterface
|
||||
{
|
||||
return $this->responseFactory->createResponse()
|
||||
->withHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
->withBody($this->streamFactory->createStream((string)json_encode($data)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?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\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Attribute\AsController;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentFactory;
|
||||
use TYPO3\CMS\Backend\Template\Components\MultiRecordSelection\Action;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
|
||||
use TYPO3\CMS\Core\Configuration\Features;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Redirects\Repository\Demand;
|
||||
use TYPO3\CMS\Redirects\Repository\RedirectRepository;
|
||||
use TYPO3\CMS\Redirects\Service\ModulePaginationService;
|
||||
use TYPO3\CMS\Redirects\Utility\RedirectConflict;
|
||||
|
||||
/**
|
||||
* Lists all Short URLs in the TYPO3 Backend as a module.
|
||||
*
|
||||
* @internal This class is a specific TYPO3 Backend controller implementation and is not part of the Public TYPO3 API.
|
||||
*/
|
||||
#[AsController]
|
||||
readonly class ShortUrlModuleController
|
||||
{
|
||||
public function __construct(
|
||||
protected UriBuilder $uriBuilder,
|
||||
protected IconFactory $iconFactory,
|
||||
protected RedirectRepository $redirectRepository,
|
||||
protected ModuleTemplateFactory $moduleTemplateFactory,
|
||||
protected ComponentFactory $componentFactory,
|
||||
protected ModulePaginationService $modulePaginationService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Injects the request object for the current request and renders the overview of all Short URLs.
|
||||
*/
|
||||
public function handleRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->moduleTemplateFactory->create($request);
|
||||
$demand = Demand::fromRequest($request);
|
||||
$redirectType = $demand->getRedirectType();
|
||||
|
||||
$view->setTitle(
|
||||
$this->getLanguageService()->translate('title', 'redirects.modules.short_urls')
|
||||
);
|
||||
|
||||
$view->makeDocHeaderModuleMenu();
|
||||
$this->registerDocHeaderButtons($view);
|
||||
|
||||
$requestUri = $request->getAttribute('normalizedParams')->getRequestUri();
|
||||
$languageService = $this->getLanguageService();
|
||||
$pagination = $this->modulePaginationService->preparePagination($demand);
|
||||
$view->assignMultiple([
|
||||
'redirects' => $this->redirectRepository->findRedirectsByDemand($demand),
|
||||
'hosts' => $this->redirectRepository->findHostsOfRedirects($redirectType),
|
||||
'protocol' => $request->getUri()->getScheme(),
|
||||
'defaultIntegrityStatus' => RedirectConflict::NO_CONFLICT,
|
||||
'demand' => $demand,
|
||||
'showHitCounter' => GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('redirects.hitCount'),
|
||||
'pagination' => $pagination,
|
||||
'returnUrl' => $this->uriBuilder->buildUriFromRoute('short_urls', [
|
||||
'page' => $pagination['current'],
|
||||
'demand' => $demand->getParameters(),
|
||||
'orderField' => $demand->getOrderField(),
|
||||
'orderDirection' => $demand->getOrderDirection(),
|
||||
]),
|
||||
'actions' => [
|
||||
new Action(
|
||||
'edit',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-open',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.edit'
|
||||
),
|
||||
new Action(
|
||||
'delete',
|
||||
[
|
||||
'idField' => 'uid',
|
||||
'tableName' => 'sys_redirect',
|
||||
'title' => $languageService->translate('delete.title', 'redirects.modules.short_urls'),
|
||||
'content' => $languageService->translate('delete.message', 'redirects.modules.short_urls'),
|
||||
'ok' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'),
|
||||
'cancel' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.cancel'),
|
||||
'returnUrl' => $requestUri,
|
||||
],
|
||||
'actions-edit-delete',
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:cm.delete'
|
||||
),
|
||||
],
|
||||
]);
|
||||
return $view->renderResponse('ShortUrl/Overview');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create document header buttons for Short URLs
|
||||
*/
|
||||
protected function registerDocHeaderButtons(ModuleTemplate $view): void
|
||||
{
|
||||
$languageService = $this->getLanguageService();
|
||||
|
||||
// Create new
|
||||
$newRecordButton = $this->componentFactory->createLinkButton()
|
||||
->setHref((string)$this->uriBuilder->buildUriFromRoute(
|
||||
'record_edit',
|
||||
[
|
||||
'edit' => ['sys_redirect' => ['new']],
|
||||
'module' => 'short_urls',
|
||||
'defVals' => [
|
||||
'sys_redirect' => [
|
||||
'redirect_type' => Demand::SHORT_URL_REDIRECT_TYPE,
|
||||
],
|
||||
],
|
||||
'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('short_urls'),
|
||||
]
|
||||
))
|
||||
->setTitle($languageService->translate('add_text', 'redirects.modules.short_urls'))
|
||||
->setShowLabelText(true)
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL));
|
||||
$view->addButtonToButtonBar($newRecordButton, ButtonBar::BUTTON_POSITION_LEFT, 10);
|
||||
|
||||
$view->getDocHeaderComponent()->setShortcutContext(
|
||||
'short_urls',
|
||||
$languageService->translate('short_description', 'redirects.modules.short_urls')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user