Skip to content

Commit

Permalink
Refactor backend module for TYPO3 12.
Browse files Browse the repository at this point in the history
  • Loading branch information
kszymukowicz committed Feb 25, 2024
1 parent 2ddeb7a commit 51a90ab
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 234 deletions.
199 changes: 72 additions & 127 deletions Classes/Controller/AdministrationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,182 +6,127 @@

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use RuntimeException;
use SourceBroker\T3api\Service\SiteService;
use TYPO3\CMS\Backend\Attribute\Controller;
use TYPO3\CMS\Backend\Module\ModuleData;
use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\Components\Menu\Menu;
use TYPO3\CMS\Backend\Template\Components\Menu\MenuItem;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;

/**
* TODO: finish refactoring for TYPO3 12
*/
#[Controller]
class AdministrationController
{
protected const SITE_SELECTOR_MENU_KEY = 'spec_site_selector_menu';

protected ModuleTemplateFactory $moduleTemplateFactory;
protected mixed $extensionConfiguration;
protected FlashMessageQueue $flashMessageQueue;
private UriBuilder $uriBuilder;
private ModuleTemplate $view;

public function __construct(
FlashMessageService $flashMessageService,
ModuleTemplateFactory $moduleTemplateFactory,
UriBuilder $uriBuilder
protected readonly FlashMessageService $flashMessage,
protected readonly UriBuilder $uriBuilder,
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
protected readonly SiteFinder $siteFinder,
protected readonly FlashMessageService $flashMessageService,
protected readonly PageRenderer $pageRenderer
) {
$this->moduleTemplateFactory = $moduleTemplateFactory;
$this->flashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
$this->uriBuilder = $uriBuilder;
}

public function handleRequest(ServerRequestInterface $request): ResponseInterface
{
return $this->documentationAction($request);
}

protected function initializeView(ServerRequestInterface $request): ModuleTemplate
/**
* @throws RouteNotFoundException
*/
public function documentationAction(ServerRequestInterface $request): ResponseInterface
{
$this->module = $request->getAttribute('module');
return $this->moduleTemplateFactory->create($request);
}
$view = $this->moduleTemplateFactory->create($request);
$moduleData = $request->getAttribute('moduleData');
$siteIdentifier = $request->getQueryParams()['site'] ?? $this->getDefaultSiteIdentifier($moduleData);
/** @var ModuleData $moduleData */
$moduleIdentifier = $request->getAttribute('module')->getIdentifier();

public function documentationAction($request, string $siteIdentifier = null): ResponseInterface
{
$this->view = $this->initializeView($request);
$siteIdentifier = $siteIdentifier ?? $this->getDefaultSiteIdentifier();
try {
$site = SiteService::getByIdentifier($siteIdentifier);
$activeSite = SiteService::getByIdentifier($siteIdentifier);
} catch (SiteNotFoundException $e) {
$site = null;
$activeSite = null;
}
$this->setUserModuleData('lastSelectedSiteIdentifier', $siteIdentifier);
$this->generateSiteSelectorMenu();
$this->setSelectedItemInSiteSelectorMenu($site);

if (!SiteService::hasT3apiRouteEnhancer($site)) {
$this->addFlashMessage(
$moduleData->set('lastSelectedSiteIdentifier', $siteIdentifier);
$this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray());

$siteSelectorMenu = $view->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
$siteSelectorMenu->setIdentifier($moduleIdentifier);
$this->generateSiteSelectorMenuItems($activeSite, $siteSelectorMenu, $moduleIdentifier);
$view->getDocHeaderComponent()->getMenuRegistry()->addMenu($siteSelectorMenu);

if (!SiteService::hasT3apiRouteEnhancer($activeSite)) {
$view->addFlashMessage(
sprintf(
'T3api route enhancer is not defined for site `%s`. Check documentation to see how to properly install t3api extension.',
$site->getIdentifier()
$activeSite->getIdentifier()
),
'',
AbstractMessage::ERROR,
'T3api route',
ContextualFeedbackSeverity::ERROR,
false
);

return $this->view->renderResponse('Administration/Documentation');
return $view->renderResponse('Administration/Documentation');
}

$this->view->assign(
'displayUrl',
(string)$this->uriBuilder->buildUriFromRoute(
$this->module->getIdentifier(),
['siteIdentifier' => $siteIdentifier],
'OpenApi'
)
$this->pageRenderer->addCssFile('EXT:t3api/Resources/Public/Css/swagger-ui.css');
$this->pageRenderer->addCssFile('EXT:t3api/Resources/Public/Css/swagger-custom.css');
$this->pageRenderer->addJsFile('EXT:t3api/Resources/Public/JavaScript/swagger-ui-bundle.js');
$this->pageRenderer->addJsFile('EXT:t3api/Resources/Public/JavaScript/swagger-ui-standalone-preset.js');
$this->pageRenderer->loadJavaScriptModule('@sourcebroker/t3api/swagger-init.js');

$view->assign(
'resourcesUrl',
$this->uriBuilder->buildUriFromRoute($moduleIdentifier . '.open_api_resources', ['site' => $siteIdentifier])
);

return $this->view->renderResponse('Administration/Documentation');
return $view->renderResponse('Administration/Documentation');
}

protected function getDefaultSiteIdentifier(): string
protected function getDefaultSiteIdentifier(ModuleData $moduleData): string
{
$sites = SiteService::getAll();
$lastSelectedSiteIdentifier = $this->getUserModuleData('lastSelectedSiteIdentifier');

$lastSelectedSiteIdentifier = $moduleData->get('lastSelectedSiteIdentifier');
if ($lastSelectedSiteIdentifier !== null && $sites[$lastSelectedSiteIdentifier] instanceof Site) {
return $sites[$lastSelectedSiteIdentifier]->getIdentifier();
}

return (SiteService::getCurrent() ?? array_shift($sites))->getIdentifier();
}

protected function generateSiteSelectorMenu(): void
{
$siteSelectorMenu = $this->view->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
$siteSelectorMenu->setIdentifier(self::SITE_SELECTOR_MENU_KEY);
foreach ($this->getSites() as $site) {
$siteSelectorMenu->addMenuItem(
$this->enrichSiteSelectorMenuItem(
$siteSelectorMenu->makeMenuItem(),
$site
)
/**
* @throws RouteNotFoundException
*/
protected function generateSiteSelectorMenuItems(
Site $activeSite,
Menu $siteSelectorMenu,
string $moduleIdentifier
): void {
foreach ($this->siteFinder->getAllSites() as $site) {
$menuItem = $siteSelectorMenu->makeMenuItem();
$host = $site->getBase()->getHost();
$menuItem->setTitle(
$site->getIdentifier() . ($host ? ' (' . $host . ')' : '')
);
}
$this->view->getDocHeaderComponent()->getMenuRegistry()->addMenu($siteSelectorMenu);
}

protected function enrichSiteSelectorMenuItem(
MenuItem $menuItem,
Site $site
): MenuItem {
$host = $site->getBase()->getHost();
$menuItem->setTitle(
$site->getIdentifier() . ($host ? ' (' . $host . ')' : '')
);
$menuItem->setHref(
(string)$this->uriBuilder->buildUriFromRoute(
$this->module->getIdentifier(),
['siteIdentifier' => $site->getIdentifier()]
)
);
$menuItem->setDataAttributes(['siteIdentifier' => $site->getIdentifier()]);

return $menuItem;
}

protected function setSelectedItemInSiteSelectorMenu(Site $site): void
{
$menu = $this->view->getDocHeaderComponent()
->getMenuRegistry()
->getMenus()[self::SITE_SELECTOR_MENU_KEY];

if (!$menu instanceof Menu) {
throw new RuntimeException(
sprintf(
'Menu `%s` is not registered',
self::SITE_SELECTOR_MENU_KEY
),
1604259496549
$menuItem->setHref(
(string)$this->uriBuilder->buildUriFromRoute(
$moduleIdentifier,
['site' => $site->getIdentifier()]
)
);
}

/** @var MenuItem $menuItem */
foreach ($menu->getMenuItems() as $menuItem) {
if ($menuItem->getDataAttributes()['siteIdentifier'] === $site->getIdentifier()) {
if ($activeSite->getIdentifier() === $site->getIdentifier()) {
$menuItem->setActive(true);
}
$siteSelectorMenu->addMenuItem($menuItem);
}
}

/**
* @return Site[]
*/
protected function getSites(): array
{
return GeneralUtility::makeInstance(SiteFinder::class)->getAllSites();
}

protected function getUserModuleData(string $variable)
{
$moduleData = $GLOBALS['BE_USER']->getModuleData('tx_t3api_m1');
return $moduleData[$variable] ?? null;
}

protected function setUserModuleData(string $variable, $value): void
protected function getBackendUser(): BackendUserAuthentication
{
$userModuleData = $GLOBALS['BE_USER']->getModuleData('tx_t3api_m1') ?? [];
$userModuleData[$variable] = $value;
$GLOBALS['BE_USER']->pushModuleData('tx_t3api_m1', $userModuleData);
return $GLOBALS['BE_USER'];
}
}
68 changes: 27 additions & 41 deletions Classes/Controller/OpenApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,62 +6,48 @@

use GoldSpecDigital\ObjectOrientedOAS\Exceptions\InvalidArgumentException as OasInvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use ReflectionException;
use Psr\Http\Message\ServerRequestInterface;
use SourceBroker\T3api\Domain\Repository\ApiResourceRepository;
use SourceBroker\T3api\Service\OpenApiBuilder;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Backend\Attribute\Controller;

/**
* TODO: finish refactoring for TYPO3 12
*/
class OpenApiController extends ActionController
#[Controller]
class OpenApiController
{
/**
* @var ApiResourceRepository
*/
protected $apiResourceRepository;

public function __construct(ApiResourceRepository $apiResourceRepository)
{
$this->apiResourceRepository = $apiResourceRepository;
public function __construct(
protected readonly ApiResourceRepository $apiResourceRepository,
protected readonly FlashMessageService $flashMessage,
protected readonly UriBuilder $uriBuilder,
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
protected readonly SiteFinder $siteFinder,
) {
}

/**
* @param string $siteIdentifier
* @throws SiteNotFoundException
*/
public function displayAction(string $siteIdentifier): ResponseInterface
{
$this->view->assign(
'specUrl',
$this->uriBuilder->reset()->uriFor('spec', ['siteIdentifier' => $siteIdentifier])
);
$this->view->assign(
'site',
GeneralUtility::makeInstance(SiteFinder::class)->getSiteByIdentifier($siteIdentifier)
);
return $this->htmlResponse();
}

/**
* @param string $siteIdentifier
* @return string
* @throws OasInvalidArgumentException
* @throws ReflectionException
* @throws SiteNotFoundException
*/
public function specAction(string $siteIdentifier): ResponseInterface
public function resourcesAction(ServerRequestInterface $request): ResponseInterface
{
$originalRequest = $GLOBALS['TYPO3_REQUEST'];
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByIdentifier($siteIdentifier);
$imitateSiteRequest = $originalRequest->withAttribute('site', $site);
$siteIdentifier = $request->getQueryParams()['site'] ?? null;
$site = $this->siteFinder->getSiteByIdentifier($siteIdentifier);

$imitateSiteRequest = $request->withAttribute('site', $site);
$GLOBALS['TYPO3_REQUEST'] = $imitateSiteRequest;
$output = OpenApiBuilder::build($this->apiResourceRepository->getAll())->toJson();
$GLOBALS['TYPO3_REQUEST'] = $originalRequest;
$GLOBALS['TYPO3_REQUEST'] = $request;

$response = new Response();
$response = $response->withHeader('Content-Type', 'application/json');
$response->getBody()->write($output);

return $this->htmlResponse($output);
return $response;
}

}
8 changes: 6 additions & 2 deletions Configuration/Backend/Modules.php
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
<?php

return [
'tools_t3api' => [
't3api' => [
'parent' => 'tools',
'position' => ['before' => '*'],
'access' => 'group,user',
'iconIdentifier' => 'extension-t3api',
'labels' => 'LLL:EXT:t3api/Resources/Private/Language/locallang_modadministration.xlf:mlang_tabs_tab',
'inheritNavigationComponentFromMainModule' => false,
'path' => '/module/t3api',
'routes' => [
'_default' => [
'target' => \SourceBroker\T3api\Controller\AdministrationController::class . '::handleRequest',
'target' => \SourceBroker\T3api\Controller\AdministrationController::class . '::documentationAction',
],
'open_api_resources' => [
'target' => \SourceBroker\T3api\Controller\OpenApiController::class . '::resourcesAction',
],
],
],
Expand Down
11 changes: 11 additions & 0 deletions Configuration/JavaScriptModules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

return [
'dependencies' => [
'core',
'backend',
],
'imports' => [
'@sourcebroker/t3api/' => 'EXT:t3api/Resources/Public/ESM/',
],
];
4 changes: 0 additions & 4 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@ services:
$apiResource: ApiResource
$params: []

SourceBroker\T3api\Controller\AdministrationController:
public: true


SourceBroker\T3api\Domain\Model\CollectionOperationFactory:
public: true

Expand Down
Loading

0 comments on commit 51a90ab

Please sign in to comment.