Commit a084a622 authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Modernize backend toolbar code

* Use BackendTemplateView
* Hand over template to render as view->render('MyTemplate')
* Move shortcut templates to Toolbar sub folder
* Move LiveSearch JS to Toolbar sub folder
* Shortcut delete and edit are POST only
* Use DI in related controllers and ToolbarItems
* Declare strict_types=1
* Return and property types in PHP classes
* PageRenderer JS module loading via fluid, not PHP
* f:translate with full LLL: keys

Change-Id: Ia8b5ecb312dd83b924955f44f53fb7b3c8415ba3
Resolves: #96519
Related: #96513
Releases: main
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72996

Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 1f6d09cc
......@@ -12,10 +12,10 @@
*/
import $ from 'jquery';
import Viewport = require('./Viewport');
import Icons = require('./Icons');
import Viewport = require('../Viewport');
import Icons = require('../Icons');
import 'jquery/autocomplete';
import './Input/Clearable';
import '../Input/Clearable';
import {html, render, TemplateResult} from 'lit';
import {unsafeHTML} from 'lit/directives/unsafe-html';
import {renderHTML} from 'TYPO3/CMS/Core/lit-helper';
......@@ -44,9 +44,9 @@ interface Suggestion {
}
/**
* Module: TYPO3/CMS/Backend/LiveSearch
* Module: TYPO3/CMS/Backend/Toolbar/LiveSearch
* Global search to deal with everything in the backend that is search-related
* @exports TYPO3/CMS/Backend/LiveSearch
* @exports TYPO3/CMS/Backend/Toolbar/LiveSearch
*/
class LiveSearch {
private url: string = TYPO3.settings.ajaxUrls.livesearch;
......
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -20,37 +22,23 @@ use TYPO3\CMS\Backend\Backend\Event\ModifyClearCacheActionsEvent;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
/**
* Render cache clearing toolbar item
* Adds a dropdown if there are more than one item to clear (usually for admins to render the flush all caches)
*
* The dropdown items can be extended via a hook named "cacheActions".
* Render cache clearing toolbar item.
* Adds a dropdown if there are more than one item to clear (usually for admins to render the flush all caches).
* The dropdown items can be manipulated using ModifyClearCacheActionsEvent.
*/
class ClearCacheToolbarItem implements ToolbarItemInterface
{
/**
* @var array
*/
protected $cacheActions = [];
/**
* @var array
*/
protected $optionValues = [];
protected array $cacheActions = [];
protected array $optionValues = [];
/**
* @throws \UnexpectedValueException
*/
public function __construct(
PageRenderer $pageRenderer,
UriBuilder $uriBuilder,
EventDispatcherInterface $eventDispatcher
) {
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar/ClearCacheMenu');
$isAdmin = $this->getBackendUser()->isAdmin();
$userTsConfig = $this->getBackendUser()->getTSConfig();
......@@ -89,11 +77,9 @@ class ClearCacheToolbarItem implements ToolbarItemInterface
}
/**
* Checks whether the user has access to this toolbar item
*
* @return bool TRUE if user has access, FALSE if not
* Checks whether the user has access to this toolbar item.
*/
public function checkAccess()
public function checkAccess(): bool
{
$backendUser = $this->getBackendUser();
if ($backendUser->isAdmin()) {
......@@ -109,92 +95,66 @@ class ClearCacheToolbarItem implements ToolbarItemInterface
/**
* Render clear cache icon, based on the option if there is more than one icon or just one.
*
* @return string Icon HTML
*/
public function getItem()
public function getItem(): string
{
if ($this->hasDropDown()) {
return $this->getFluidTemplateObject('ClearCacheToolbarItem.html')->render();
return $this->getFluidTemplateObject()->render('ToolbarItems/ClearCacheToolbarItem');
}
$view = $this->getFluidTemplateObject('ClearCacheToolbarItemSingle.html');
$view = $this->getFluidTemplateObject();
$cacheAction = end($this->cacheActions);
$view->assignMultiple([
'link' => $cacheAction['href'],
'title' => $cacheAction['title'],
'iconIdentifier' => $cacheAction['iconIdentifier'],
]);
return $view->render();
return $view->render('ToolbarItems/ClearCacheToolbarItemSingle');
}
/**
* Render drop down
*
* @return string Drop down HTML
* Render drop-down.
*/
public function getDropDown()
public function getDropDown(): string
{
$view = $this->getFluidTemplateObject('ClearCacheToolbarItemDropDown.html');
$view = $this->getFluidTemplateObject();
$view->assign('cacheActions', $this->cacheActions);
return $view->render();
return $view->render('ToolbarItems/ClearCacheToolbarItemDropDown');
}
/**
* No additional attributes needed.
*
* @return array
*/
public function getAdditionalAttributes()
public function getAdditionalAttributes(): array
{
return [];
}
/**
* This item has a drop down if there is more than one cache action available for the current Backend user.
*
* @return bool
* This item has a drop-down, if there is more than one cache action available for the current Backend user.
*/
public function hasDropDown()
public function hasDropDown(): bool
{
return count($this->cacheActions) > 1;
}
/**
* Position relative to others
*
* @return int
*/
public function getIndex()
public function getIndex(): int
{
return 25;
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication
*/
protected function getBackendUser()
protected function getFluidTemplateObject(): BackendTemplateView
{
return $GLOBALS['BE_USER'];
$view = GeneralUtility::makeInstance(BackendTemplateView::class);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials']);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates']);
return $view;
}
/**
* Returns a new standalone view, shorthand function
*
* @param string $filename Which templateFile should be used.
* @return StandaloneView
*/
protected function getFluidTemplateObject(string $filename): StandaloneView
protected function getBackendUser(): BackendUserAuthentication
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials/ToolbarItems']);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates/ToolbarItems']);
$view->setTemplate($filename);
$view->getRequest()->setControllerExtensionName('Backend');
return $view;
return $GLOBALS['BE_USER'];
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -19,24 +21,18 @@ use TYPO3\CMS\Backend\Domain\Model\Module\BackendModule;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
/**
* Help toolbar item
* Help toolbar item - The question mark icon in toolbar
*/
class HelpToolbarItem implements ToolbarItemInterface
{
/**
* @var BackendModule
*/
protected $helpModuleMenu;
protected ?BackendModule $helpModuleMenu = null;
/**
* Constructor
*/
public function __construct()
{
$backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class);
public function __construct(
BackendModuleRepository $backendModuleRepository
) {
$helpModuleMenu = $backendModuleRepository->findByModuleName('help');
if ($helpModuleMenu && $helpModuleMenu->getChildren()->count() > 0) {
$this->helpModuleMenu = $helpModuleMenu;
......@@ -45,81 +41,63 @@ class HelpToolbarItem implements ToolbarItemInterface
/**
* Users see this if a module is available
*
* @return bool TRUE
*/
public function checkAccess()
public function checkAccess(): bool
{
return (bool)$this->helpModuleMenu;
}
/**
* Render help icon
*
* @return string toolbar item for the help icon
*/
public function getItem()
public function getItem(): string
{
return $this->getFluidTemplateObject('HelpToolbarItem.html')->render();
return $this->getFluidTemplateObject()->render('ToolbarItems/HelpToolbarItem');
}
/**
* Render drop down
*
* @return string
*/
public function getDropDown()
public function getDropDown(): string
{
$view = $this->getFluidTemplateObject('HelpToolbarItemDropDown.html');
if (!$this->helpModuleMenu instanceof BackendModule) {
// checkAccess() is called before and prevents call to getDropDown() if there is no help.
throw new \RuntimeException('No HelpModuleMenu found.', 1641993564);
}
$view = $this->getFluidTemplateObject();
$view->assign('modules', $this->helpModuleMenu->getChildren());
return $view->render();
return $view->render('ToolbarItems/HelpToolbarItemDropDown');
}
/**
* No additional attributes needed.
*
* @return array
*/
public function getAdditionalAttributes()
public function getAdditionalAttributes(): array
{
return [];
}
/**
* This item has a drop down
*
* @return bool
* This item has a drop-down
*/
public function hasDropDown()
public function hasDropDown(): bool
{
return true;
}
/**
* Position relative to others
*
* @return int
*/
public function getIndex()
public function getIndex(): int
{
return 70;
}
/**
* Returns a new standalone view, shorthand function
*
* @param string $filename Which templateFile should be used.
* @return StandaloneView
*/
protected function getFluidTemplateObject(string $filename): StandaloneView
protected function getFluidTemplateObject(): BackendTemplateView
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials/ToolbarItems']);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates/ToolbarItems']);
$view->setTemplate($filename);
$view->getRequest()->setControllerExtensionName('Backend');
$view = GeneralUtility::makeInstance(BackendTemplateView::class);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials']);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates']);
return $view;
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -17,113 +19,71 @@ namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
/**
* Adds backend live search to the toolbar by adding JavaScript and adding an input search field
*/
class LiveSearchToolbarItem implements ToolbarItemInterface
{
/**
* Loads the needed JavaScript file, ands includes it to the page renderer
*/
public function __construct()
{
$this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/LiveSearch');
protected BackendModuleRepository $backendModuleRepository;
public function __construct(
BackendModuleRepository $backendModuleRepository
) {
$this->backendModuleRepository = $backendModuleRepository;
}
/**
* Checks whether the user has access to this toolbar item,
* only allowed when the list module is available.
* Live search is heavily dependent on the list module and only available when that module is.
*
* @return bool TRUE if user has access, FALSE if not
* Checks whether the user has access to this toolbar item.
* Live search depends on the list module and only available when that module is allowed.
*/
public function checkAccess()
public function checkAccess(): bool
{
$backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class);
$listModule = $backendModuleRepository->findByModuleName('web_list');
$listModule = $this->backendModuleRepository->findByModuleName('web_list');
return $listModule !== null && $listModule !== false;
}
/**
* Render search field
*
* @return string Live search form HTML
* Render search field.
*/
public function getItem()
public function getItem(): string
{
return $this->getFluidTemplateObject('LiveSearchToolbarItem.html')->render();
$view = GeneralUtility::makeInstance(BackendTemplateView::class);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates']);
return $view->render('ToolbarItems/LiveSearchToolbarItem');
}
/**
* This item needs to additional attributes
*
* @return array
* This item needs additional attributes.
*/
public function getAdditionalAttributes()
public function getAdditionalAttributes(): array
{
return ['class' => 'toolbar-item-search t3js-toolbar-item-search'];
}
/**
* This item has no drop down
*
* @return bool
* This item has no drop-down.
*/
public function hasDropDown()
public function hasDropDown(): bool
{
return false;
}
/**
* No drop down here
*
* @return string
* No drop-down here.
*/
public function getDropDown()
public function getDropDown(): string
{
return '';
}
/**
* Position relative to others, live search should be very right
*
* @return int
* Position relative to others, live search should be very right.
*/
public function getIndex()
public function getIndex(): int
{
return 90;
}
/**
* Returns current PageRenderer
*
* @return PageRenderer
*/
protected function getPageRenderer()
{
return GeneralUtility::makeInstance(PageRenderer::class);
}
/**
* Returns a new standalone view, shorthand function
*
* @param string $filename Which templateFile should be used.
* @return StandaloneView
*/
protected function getFluidTemplateObject(string $filename): StandaloneView
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts']);
$view->setPartialRootPaths(['EXT:backend/Resources/Private/Partials/ToolbarItems']);
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates/ToolbarItems']);
$view->setTemplate($filename);
$view->getRequest()->setControllerExtensionName('Backend');
return $view;
}
}
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
......@@ -17,85 +19,57 @@ namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
use TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
/**
* Class to render the shortcut menu
* Class to render the shortcut menu toolbar.
*
* @internal This class is a specific Backend implementation and is not considered part of the Public TYPO3 API.
*/
class ShortcutToolbarItem implements ToolbarItemInterface
{
/**
* @var ShortcutRepository
*/
protected $shortcutRepository;
/**
* Constructor
*/
public function __construct()
{
$this->shortcutRepository = GeneralUtility::makeInstance(ShortcutRepository::class);
protected ShortcutRepository $shortcutRepository;
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Toolbar/ShortcutMenu');
$languageService = $this->getLanguageService();
$languageFile = 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf';
$pageRenderer->addInlineLanguageLabelArray([
'bookmark.delete' => $languageService->sL($languageFile . ':toolbarItems.bookmarksDelete'),
'bookmark.confirmDelete' => $languageService->sL($languageFile . ':toolbarItems.confirmBookmarksDelete'),
'bookmark.create' => $languageService->sL($languageFile . ':toolbarItems.createBookmark'),
'bookmark.savedTitle' => $languageService->sL($languageFile . ':toolbarItems.bookmarkSavedTitle'),
'bookmark.savedMessage' => $languageService->sL($languageFile . ':toolbarItems.bookmarkSavedMessage'),
]);
public function __construct(
ShortcutRepository $shortcutRepository
) {
$this->shortcutRepository = $shortcutRepository;
}
/**
* Checks whether the user has access to this toolbar item
*
* @return bool TRUE if user has access, FALSE if not
* Checks whether the user has access to this toolbar item.
*/
public function checkAccess()
public function checkAccess(): bool
{
return (bool)($this->getBackendUser()->getTSConfig()['options.']['enableBookmarks'] ?? false);
}
/**
* Render shortcut icon
*
* @return string HTML
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidExtensionNameException
* @throws \InvalidArgumentException
* Render shortcut icon.
*/
public function getItem()
public function getItem(): string
{
return $this->getFluidTemplateObject('Item.html')->render();
return $this->getFluidTemplateObject()->render('ToolbarItems/ShortcutToolbarItemItem');
}
/**
* This item has a drop down
*
* @return bool
* This item has a drop-down.
*/
public function hasDropDown()
public function hasDropDown(): bool
{
return true;
}
/**
* Render drop down content
*
* @return string HTML
* Render drop-down content
*/
public function getDropDown()
public function getDropDown(): string
{