first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,102 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Controller;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
use TYPO3\CMS\Adminpanel\Service\ModuleLoader;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Admin Panel Ajax Controller - Route endpoint for ajax actions
*
* @internal
*/
class AjaxController
{
/**
* @var array
*/
protected $adminPanelModuleConfiguration;
/**
* @var ModuleLoader
*/
protected $moduleLoader;
/**
* @var ConfigurationService
*/
private $configurationService;
/**
* @param ConfigurationService $configurationService
* @param ModuleLoader $moduleLoader
*/
public function __construct(ConfigurationService $configurationService = null, ModuleLoader $moduleLoader = null)
{
$this->configurationService = $configurationService
??
GeneralUtility::makeInstance(ConfigurationService::class);
$this->adminPanelModuleConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules'] ?? [];
$this->moduleLoader = $moduleLoader ?? GeneralUtility::makeInstance(ModuleLoader::class);
}
/**
* Save adminPanel data
*
* @param ServerRequestInterface $request
* @return JsonResponse
*/
public function saveDataAction(ServerRequestInterface $request): JsonResponse
{
$this->configurationService->saveConfiguration(
$this->moduleLoader->validateSortAndInitializeModules($this->adminPanelModuleConfiguration),
$request
);
return new JsonResponse(['success' => true]);
}
/**
* Toggle admin panel active state via UC
*
* @return JsonResponse
*/
public function toggleActiveState(): JsonResponse
{
$backendUser = $this->getBackendUser();
if ($backendUser->uc['AdminPanel']['display_top'] ?? false) {
$backendUser->uc['AdminPanel']['display_top'] = false;
} else {
$backendUser->uc['AdminPanel']['display_top'] = true;
}
$backendUser->writeUC();
return new JsonResponse(['success' => true]);
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,243 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Controller;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ConfigurableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleDataStorageCollection;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\PageSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ShortInfoProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\SubmoduleProviderInterface;
use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
use TYPO3\CMS\Adminpanel\Service\ModuleLoader;
use TYPO3\CMS\Adminpanel\Utility\ResourceUtility;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Adminpanel\View\AdminPanelView;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Main controller for the admin panel
*
* @internal
*/
class MainController implements SingletonInterface
{
/**
* @var \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[]
*/
protected $modules = [];
/**
* @var ModuleLoader
*/
protected $moduleLoader;
/**
* @var UriBuilder
*/
protected $uriBuilder;
/**
* @var ConfigurationService
*/
protected $configurationService;
/**
* @var array
*/
protected $adminPanelModuleConfiguration;
/**
* @param ModuleLoader $moduleLoader
* @param UriBuilder $uriBuilder
* @param ConfigurationService $configurationService
*/
public function __construct(
ModuleLoader $moduleLoader = null,
UriBuilder $uriBuilder = null,
ConfigurationService $configurationService = null
) {
$this->moduleLoader = $moduleLoader ?? GeneralUtility::makeInstance(ModuleLoader::class);
$this->uriBuilder = $uriBuilder ?? GeneralUtility::makeInstance(UriBuilder::class);
$this->configurationService = $configurationService
??
GeneralUtility::makeInstance(ConfigurationService::class);
$this->adminPanelModuleConfiguration = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules'] ?? [];
}
/**
* Initializes settings for the admin panel.
*
* @param ServerRequestInterface $request
*/
public function initialize(ServerRequestInterface $request): void
{
$this->modules = $this->moduleLoader->validateSortAndInitializeModules(
$this->adminPanelModuleConfiguration
);
if (StateUtility::isActivatedForUser()) {
$this->initializeModules($request, $this->modules);
}
}
/**
* Renders the admin panel - Called in PSR-15 Middleware
*
* @see \TYPO3\CMS\Adminpanel\Middleware\AdminPanelRenderer
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return string
*/
public function render(ServerRequestInterface $request): string
{
// legacy handling, deprecated, will be removed in TYPO3 v10.0.
$adminPanelView = GeneralUtility::makeInstance(AdminPanelView::class);
$hookObjectContent = $adminPanelView->callDeprecatedHookObject();
// end legacy handling
$resources = ResourceUtility::getResources();
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Main.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->setLayoutRootPaths(['EXT:adminpanel/Resources/Private/Layouts']);
$view->assignMultiple(
[
'toggleActiveUrl' => $this->generateBackendUrl('ajax_adminPanel_toggle'),
'resources' => $resources,
'adminPanelActive' => StateUtility::isOpen(),
]
);
if (StateUtility::isOpen()) {
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('adminpanel_requestcache');
$requestId = $request->getAttribute('adminPanelRequestId');
$data = $cache->get($requestId);
$moduleResources = ResourceUtility::getAdditionalResourcesForModules($this->modules);
$settingsModules = array_filter($this->modules, function (ModuleInterface $module) {
return $module instanceof PageSettingsProviderInterface;
});
$parentModules = array_filter(
$this->modules,
function (ModuleInterface $module) {
return $module instanceof SubmoduleProviderInterface && $module instanceof ShortInfoProviderInterface;
}
);
$view->assignMultiple(
[
'modules' => $this->modules,
'settingsModules' => $settingsModules,
'parentModules' => $parentModules,
'hookObjectContent' => $hookObjectContent,
'saveUrl' => $this->generateBackendUrl('ajax_adminPanel_saveForm'),
'moduleResources' => $moduleResources,
'requestId' => $requestId,
'data' => $data ?? [],
]
);
}
return $view->render();
}
/**
* Stores data for admin panel in cache - Called in PSR-15 Middleware
*
* @see \TYPO3\CMS\Adminpanel\Middleware\AdminPanelDataPersister
* @param \Psr\Http\Message\ServerRequestInterface $request
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function storeData(ServerRequestInterface $request): void
{
if (StateUtility::isOpen()) {
$data = $this->storeDataPerModule(
$request,
$this->modules,
GeneralUtility::makeInstance(ModuleDataStorageCollection::class)
);
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('adminpanel_requestcache');
$cache->set($request->getAttribute('adminPanelRequestId'), $data);
$cache->collectGarbage();
}
}
/**
* Generate a url to a backend route
*
* @param string $route
* @return string
*/
protected function generateBackendUrl(string $route): string
{
return (string)$this->uriBuilder->buildUriFromRoute($route);
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[] $modules
*/
protected function initializeModules(ServerRequestInterface $request, array $modules): void
{
foreach ($modules as $module) {
if (
($module instanceof InitializableInterface)
&& (
(($module instanceof ConfigurableInterface) && $module->isEnabled())
|| (!($module instanceof ConfigurableInterface))
)
) {
$module->initializeModule($request);
}
if ($module instanceof SubmoduleProviderInterface) {
$this->initializeModules($request, $module->getSubModules());
}
}
}
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[] $modules
* @param ModuleDataStorageCollection $data
* @return ModuleDataStorageCollection
*/
protected function storeDataPerModule(ServerRequestInterface $request, array $modules, ModuleDataStorageCollection $data): ModuleDataStorageCollection
{
foreach ($modules as $module) {
if (
($module instanceof DataProviderInterface)
&& (
(($module instanceof ConfigurableInterface) && $module->isEnabled())
|| (!($module instanceof ConfigurableInterface))
)
) {
$data->addModuleData($module, $module->getDataToStore($request));
}
if ($module instanceof SubmoduleProviderInterface) {
$this->storeDataPerModule($request, $module->getSubModules(), $data);
}
}
return $data;
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Exceptions;
/*
* 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!
*/
/**
* Exception class for invalid admin panel configuration
*/
class InvalidConfigurationException extends \RuntimeException
{
}
@@ -0,0 +1,100 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Log;
/*
* 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!
*/
use Doctrine\DBAL\Logging\SQLLogger;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use TYPO3\CMS\Adminpanel\Utility\MemoryUtility;
/**
* Doctrine SQL Logger implementation for recording queries for the admin panel
*/
class DoctrineSqlLogger implements SQLLogger, LoggerAwareInterface
{
use LoggerAwareTrait;
/**
* Executed SQL queries.
*
* @var array
*/
protected $queries = [];
/**
* If Debug Stack is enabled (log queries) or not.
*
* @var bool
*/
protected $enabled = true;
/**
* @var float
*/
protected $start;
/**
* @var int
*/
protected $currentQuery = 0;
/**
* {@inheritdoc}
*/
public function startQuery($sql, array $params = null, array $types = null)
{
if ($this->enabled && MemoryUtility::isMemoryConsumptionTooHigh()) {
$this->enabled = false;
$this->logger->warning('SQL Logging consumed too much memory, aborted. Not all queries have been recorded.');
}
if ($this->enabled) {
$this->start = microtime(true);
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 7);
// remove this method
array_shift($backtrace);
// remove doctrine execute query
array_shift($backtrace);
// remove queryBuilder execute
array_shift($backtrace);
$this->queries[++$this->currentQuery] = [
'sql' => $sql,
'params' => $params,
'types' => $types,
'executionMS' => 0,
'backtrace' => $backtrace
];
}
}
/**
* {@inheritdoc}
*/
public function stopQuery()
{
if ($this->enabled) {
$this->queries[$this->currentQuery]['executionMS'] = microtime(true) - $this->start;
}
}
/**
* @return array
*/
public function getQueries(): array
{
return $this->queries;
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Log;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\Utility\MemoryUtility;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Log\LogRecord;
use TYPO3\CMS\Core\Log\Writer\AbstractWriter;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Log writer that writes the log records into a static public class variable
* for InMemory processing
*/
class InMemoryLogWriter extends AbstractWriter
{
public static $log = [];
private static $memoryLock = false;
/**
* Writes the log record
*
* @param LogRecord $record Log record
* @return self
* @throws \RuntimeException
*/
public function writeLog(LogRecord $record): self
{
// Guard: Locked Writer
if (self::$memoryLock === true) {
return $this;
}
// Guard: Memory Usage
if (!self::$memoryLock && MemoryUtility::isMemoryConsumptionTooHigh()) {
$this->lockWriter();
return $this;
}
self::$log[] = $record;
return $this;
}
/**
* Lock writer and add a info message that there may potentially be more entries.
*/
protected function lockWriter(): void
{
self::$memoryLock = true;
/** @var LogRecord $record */
$record = GeneralUtility::makeInstance(
LogRecord::class,
'TYPO3.CMS.AdminPanel.Log.InMemoryLogWriter',
LogLevel::INFO,
'... Further log entries omitted, memory usage too high.'
);
self::$log[] = $record;
}
}
@@ -0,0 +1,58 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Middleware;
/*
* 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!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Adminpanel\Controller\MainController;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Core\Http\NullResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Store request data of current request in cache for rendering in admin panel
*
* @internal
*/
class AdminPanelDataPersister implements MiddlewareInterface
{
/**
* Render the admin panel if activated
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);
if (
!($response instanceof NullResponse)
&& $GLOBALS['TSFE'] instanceof TypoScriptFrontendController
&& $GLOBALS['TSFE']->isOutputting()
&& StateUtility::isActivatedForUser()
&& StateUtility::isActivatedInTypoScript()
&& !StateUtility::isHiddenForUser()
) {
$mainController = GeneralUtility::makeInstance(MainController::class);
$mainController->storeData($request);
}
return $response;
}
}
@@ -0,0 +1,60 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Middleware;
/*
* 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!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Adminpanel\Controller\MainController;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Adminpanel\View\AdminPanelView;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* PSR-15 Middleware to initialize the admin panel
*
* @internal
*/
class AdminPanelInitiator implements MiddlewareInterface
{
/**
* Initialize the adminPanel if
* - backend user is logged in
* - at least one adminpanel functionality is enabled
* - admin panel is open
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (StateUtility::isActivatedForUser() && StateUtility::isOpen()) {
$request = $request->withAttribute('adminPanelRequestId', substr(md5(uniqid('', true)), 0, 13));
$adminPanelController = GeneralUtility::makeInstance(
MainController::class
);
$adminPanelController->initialize($request);
// legacy handling
$beUser = $GLOBALS['BE_USER'];
$beUser->adminPanel = GeneralUtility::makeInstance(AdminPanelView::class);
$beUser->extAdmEnabled = true;
}
return $handler->handle($request);
}
}
@@ -0,0 +1,70 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Middleware;
/*
* 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!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Adminpanel\Controller\MainController;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Core\Http\NullResponse;
use TYPO3\CMS\Core\Http\Stream;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Render the admin panel via PSR-15 middleware
*
* @internal
*/
class AdminPanelRenderer implements MiddlewareInterface
{
/**
* Render the admin panel if activated
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);
if (
!($response instanceof NullResponse)
&& $GLOBALS['TSFE'] instanceof TypoScriptFrontendController
&& $GLOBALS['TSFE']->isOutputting()
&& StateUtility::isActivatedForUser()
&& StateUtility::isActivatedInTypoScript()
&& !StateUtility::isHiddenForUser()
) {
$mainController = GeneralUtility::makeInstance(MainController::class);
$body = $response->getBody();
$body->rewind();
$contents = $response->getBody()->getContents();
$content = str_ireplace(
'</body>',
$mainController->render($request) . '</body>',
$contents
);
$body = new Stream('php://temp', 'rw');
$body->write($content);
$response = $response->withBody($body);
}
return $response;
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Middleware;
/*
* 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!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Adminpanel\Log\DoctrineSqlLogger;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Enable sql logging for the admin panel
*
* @internal
*/
class SqlLogging implements MiddlewareInterface
{
/**
* Enable SQL Logging as early as possible to catch all queries if the admin panel is active
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (StateUtility::isActivatedForUser() && StateUtility::isOpen()) {
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$connection = $connectionPool->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
$connection->getConfiguration()->setSQLLogger(GeneralUtility::makeInstance(DoctrineSqlLogger::class));
}
return $handler->handle($request);
}
}
@@ -0,0 +1,144 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Abstract base class for Admin Panel Modules containing helper methods and default interface implementations
* Extend this class when writing own admin panel modules (or implement the Interface directly)
*/
abstract class AbstractModule implements ModuleInterface, ConfigurableInterface, SubmoduleProviderInterface
{
/**
* @var ModuleInterface[]
*/
protected $subModules = [];
/**
* Main Configuration (from UserTSConfig, admPanel)
*
* @var array
*/
protected $mainConfiguration;
/**
* @var ConfigurationService
*/
protected $configurationService;
public function __construct()
{
$this->configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
$this->mainConfiguration = $this->configurationService->getMainConfiguration();
}
/**
* Returns true if the module is
* -> either enabled via TSConfig admPanel.enable
* -> or any setting is overridden
* override is a way to use functionality of the admin panel without displaying the admin panel to users
* for example: hidden records or pages can be displayed by default
*
* @return bool
*/
public function isEnabled(): bool
{
$identifier = $this->getIdentifier();
$result = $this->isEnabledViaTsConfig();
if ($this->mainConfiguration['override.'][$identifier] ?? false) {
$result = (bool)$this->mainConfiguration['override.'][$identifier];
}
return $result;
}
/**
* @inheritdoc
*/
public function setSubModules(array $subModules): void
{
$this->subModules = $subModules;
}
/**
* @inheritdoc
*/
public function getSubModules(): array
{
return $this->subModules;
}
/**
* @inheritdoc
*/
public function hasSubmoduleSettings(): bool
{
$hasSettings = false;
foreach ($this->subModules as $subModule) {
if ($subModule instanceof ModuleSettingsProviderInterface) {
$hasSettings = true;
break;
}
if ($subModule instanceof SubmoduleProviderInterface) {
$hasSettings = $subModule->hasSubmoduleSettings();
}
}
return $hasSettings;
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication|FrontendBackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
/**
* Returns LanguageService
*
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
/**
* Returns true if TSConfig admPanel.enable is set for this module (or all modules)
*
* @return bool
*/
protected function isEnabledViaTsConfig(): bool
{
$result = false;
$identifier = $this->getIdentifier();
if (!empty($this->mainConfiguration['enable.']['all'])) {
$result = true;
} elseif (!empty($this->mainConfiguration['enable.'][$identifier])) {
$result = true;
}
return $result;
}
}
@@ -0,0 +1,37 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
use TYPO3\CMS\Core\Localization\LanguageService;
/**
* Abstract SubModule - Base class for sub modules in the admin panel
*
* Extend this class when writing own sub modules
*/
abstract class AbstractSubModule implements ModuleInterface, ContentProviderInterface
{
/**
* Returns LanguageService
*
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* AdminPanel ConfigurableInterface
*
* Used to indicate that an adminpanel module can be enabled or disabled via configuration
*
* Usual implementation is done via user tsconfig
* @see \TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule::isEnabled()
*/
interface ConfigurableInterface
{
/**
* Module is enabled
* -> should be initialized
* A module may be enabled but not shown
* -> only the initializeModule() method
* will be called
*
* @return bool
*/
public function isEnabled(): bool;
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel interface to denote that a module has content to be rendered
*
* In general there are two main types of admin panel modules: those providing settings influencing page rendering
* (for example the preview module provides settings for displaying hidden pages or records) and those rendering
* information regarding the current request (for example the log module).
*
* Modules implementing this interface denote that they render module content.
*/
interface ContentProviderInterface
{
/**
* Main method for content generation of an admin panel module.
* Return content as HTML. For modules implementing the DataProviderInterface
* the "ModuleData" object is automatically filled with the stored data - if
* no data is given a "fresh" ModuleData object is injected.
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
* @return string
*/
public function getContent(ModuleData $data): string;
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
/**
* Adminpanel interface to denote that a module provides data to be stored for the current request
*
* Adminpanel modules can save data to the adminpanel request cache and access this data in the rendering process.
* Data necessary for rendering the module content has to be returned via this interface implementation, as this allows
* for separate data collection and rendering and is a pre-requisite for a standalone debug tool.
*/
interface DataProviderInterface
{
/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData;
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
/**
* Adminpanel interface to denote that a module has tasks to perform on initialization of the request
*
* Modules that need to set data / options early in the rendering process to be able to collect data, should implement
* this interface - for example the log module uses the initialization to register the admin panel log collection early
* in the rendering process.
*
* Initialize is called in the PSR-15 middleware stack through admin panel initialisation via the AdminPanel MainController.
*
* @see \TYPO3\CMS\Adminpanel\Middleware\AdminPanelInitiator::process()
* @see \TYPO3\CMS\Adminpanel\Controller\MainController::initialize()
*/
interface InitializableInterface
{
/**
* Initialize the module - runs early in a TYPO3 request
*
* @param ServerRequestInterface $request
*/
public function initializeModule(ServerRequestInterface $request): void;
}
@@ -0,0 +1,27 @@
<?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\Adminpanel\ModuleApi;
/**
* ModuleData is a simple wrapper object which extends ArrayObject
* which is used to hold Adminpanel module data
*
* It's a separate class to add semantic meaning to its' usage
*/
class ModuleData extends \ArrayObject
{
}
@@ -0,0 +1,39 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* ModuleDataStorageCollection is an object storage for adminpanel modules and their data
*/
class ModuleDataStorageCollection extends \SplObjectStorage
{
public function addModuleData(DataProviderInterface $module, ModuleData $moduleData): void
{
$this->attach($module, $moduleData);
}
public function getHash($object)
{
if ($object instanceof ModuleInterface) {
return $object->getIdentifier();
}
throw new \InvalidArgumentException(
'Only modules implementing ' . ModuleInterface::class . ' are allowed to be stored',
1535301628
);
}
}
@@ -0,0 +1,40 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel interface for all modules
*
* Every adminpanel module needs to implement this interface as a bare minimum.
*/
interface ModuleInterface
{
/**
* Identifier for this module,
* for example "preview" or "cache"
*
* @return string
*/
public function getIdentifier(): string;
/**
* Module label
*
* @return string
*/
public function getLabel(): string;
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel module settings interface denotes that a module has own settings.
*
* The adminpanel knows two types of settings:
* - ModuleSettings are relevant for the module itself and its representation (for example the log module provides settings
* where displayed log level and grouping of the module can be configured)
* - PageSettings are relevant for rendering the page (for example the preview module provides settings showing or hiding
* hidden content elements or simulating a specific rendering time)
*
* If a module provides settings relevant to its own content, use this interface.
*
* @see \TYPO3\CMS\Adminpanel\ModuleApi\PageSettingsProviderInterface
*/
interface ModuleSettingsProviderInterface
{
/**
* @return string
*/
public function getSettings(): string;
}
@@ -0,0 +1,35 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
/**
* Adminpanel interface for modules that need to react on changed configuration
* (for example if fluid debug settings change, the frontend cache should be cleared)
*/
interface OnSubmitActorInterface
{
/**
* Executed on saving / submit of the configuration form
* Can be used to react to changed settings
* (for example: clearing a specific cache)
*
* @param array $configurationToSave
* @param ServerRequestInterface $request
*/
public function onSubmit(array $configurationToSave, ServerRequestInterface $request): void;
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel page settings interface denotes that a module has settings regarding the page rendering.
* The adminpanel knows two types of settings:
* - ModuleSettings are relevant for the module itself and its representation (for example the log module provides settings
* where displayed log level and grouping of the module can be configured)
* - PageSettings are relevant for rendering the page (for example the preview module provides settings showing or hiding
* hidden content elements or simulating a specific rendering time)
* If a module provides settings changing the rendering of the main page request, use this interface.
*
* @see \TYPO3\CMS\Adminpanel\ModuleApi\ModuleSettingsProviderInterface
*/
interface PageSettingsProviderInterface
{
/**
* @return string
*/
public function getPageSettings(): string;
}
@@ -0,0 +1,44 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel interface to denote that a module has own resource files.
*
* An adminpanel module implementing this interface may deliver custom JavaScript and Css files to provide additional
* styling and JavaScript functionality
*/
interface ResourceProviderInterface
{
/**
* Returns a string array with javascript files that will be rendered after the module
*
* Example: return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.js'];
*
* @return array
*/
public function getJavaScriptFiles(): array;
/**
* Returns a string array with css files that will be rendered after the module
*
* Example: return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.css'];
*
* @return array
*/
public function getCssFiles(): array;
}
@@ -0,0 +1,43 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel shortinfo provider interface can be used to add the module to the short info bar of the adminpanel
*
* Modules providing shortinfo will be displayed in the bottom bar of the adminpanel and may provide "at a glance" info
* about the current state (for example the log module provides the number of warnings and errors directly).
*
* Be aware that modules with submodules at the moment can only render one short info (the one of the "parent" module).
* This will likely change in TYPO3 v10.0.
*/
interface ShortInfoProviderInterface
{
/**
* Displayed directly in the bar
*
* @return string
*/
public function getShortInfo(): string;
/**
* Icon identifier - needs to be registered in iconRegistry
*
* @return string
*/
public function getIconIdentifier(): string;
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ModuleApi;
/*
* 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!
*/
/**
* Adminpanel interface providing hierarchical functionality for modules
*
* A module implementing this interface may have submodules. Be aware that the current implementation of the adminpanel
* renders a maximum level of 2 for modules. If you need to render more levels, write your own module and implement
* multi-hierarchical rendering in the getContent method
*
* @see \TYPO3\CMS\Adminpanel\ModuleApi\ContentProviderInterface::getContent()
*/
interface SubmoduleProviderInterface
{
/**
* Sets array of module instances (instances of `ModuleInterface`) as submodules
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[] $subModules
*/
public function setSubModules(array $subModules): void;
/**
* Returns an array of module instances
*
* @return \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[]
*/
public function getSubModules(): array;
/**
* Return true if any of the submodules has settings to be rendered
* (can be used to render settings in a central place)
*
* @return bool
*/
public function hasSubmoduleSettings(): bool;
}
@@ -0,0 +1,124 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\PageSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
class CacheModule extends AbstractModule implements PageSettingsProviderInterface, InitializableInterface, ResourceProviderInterface
{
/**
* @return string
*/
public function getIconIdentifier(): string
{
return 'apps-toolbar-menu-cache';
}
/**
* @return string
*/
public function getPageSettings(): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Settings/Cache.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$feCacheClear = $this->getBackendUser()->isAdmin() || $this->getBackendUser()->getTSConfig()['options.']['clearCache.']['pages'];
$view->assignMultiple(
[
'isEnabled' => $this->getBackendUser()->uc['AdminPanel']['display_cache'],
'noCache' => $this->getBackendUser()->uc['AdminPanel']['cache_noCache'],
'currentId' => $this->getTypoScriptFrontendController()->id,
'clearPageCacheUrl' => $feCacheClear ? (string)$uriBuilder->buildUriFromRoute('tce_db', ['cacheCmd' => 'pages']) : '',
'clearCurrentPageCacheUrl' => (string)$uriBuilder->buildUriFromRoute(
'tce_db',
[
'cacheCmd' => $this->getTypoScriptFrontendController()->id,
]
),
]
);
return $view->render();
}
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'cache';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
$locallangFileAndPath = 'LLL:EXT:adminpanel/Resources/Private/Language/locallang_cache.xlf:module.label';
return $this->getLanguageService()->sL($locallangFileAndPath);
}
/**
* @inheritdoc
*/
public function initializeModule(ServerRequestInterface $request): void
{
if ($this->configurationService->getConfigurationOption('cache', 'noCache')) {
$this->getTypoScriptFrontendController()->set_no_cache('Admin Panel: No Caching', true);
}
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
/**
* @return array
*/
public function getJavaScriptFiles(): array
{
return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Cache.js'];
}
/**
* Returns a string array with css files that will be rendered after the module
*
* Example: return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.css'];
*
* @return array
*/
public function getCssFiles(): array
{
return [];
}
}
@@ -0,0 +1,208 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\Debug;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\Log\InMemoryLogWriter;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Log Sub Module of the AdminPanel
*
* @internal
*/
class Log extends AbstractSubModule implements DataProviderInterface, ModuleSettingsProviderInterface, InitializableInterface
{
protected $logLevel = LogLevel::INFO;
/**
* @var ConfigurationService
*/
protected $configurationService;
public function __construct()
{
$this->configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
}
/**
* @return string
*/
public function getIdentifier(): string
{
return 'debug_log';
}
/**
* Sub-Module label
*
* @return string
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:submodule.log.label'
);
}
/**
* @inheritdoc
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
$levels = [];
for ($i = 1; $i <= LogLevel::DEBUG; $i++) {
$levels[] = [
'level' => $i,
'levelName' => LogLevel::getName($i),
];
}
$log = InMemoryLogWriter::$log;
$logArray = [];
/** @var \TYPO3\CMS\Core\Log\LogRecord $logRecord */
foreach ($log as $logRecord) {
$entry = $logRecord->toArray();
// store only necessary info
unset($entry['data']);
$logArray[] = $entry;
}
return new ModuleData(
[
'levels' => $levels,
'startLevel' => (int)$this->getConfigOption('startLevel'),
'log' => $logArray,
]
);
}
/**
* @inheritdoc
*/
public function getSettings(): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Debug/LogSettings.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$levels = [];
for ($i = 1; $i <= LogLevel::DEBUG; $i++) {
$levels[] = [
'level' => $i,
'levelName' => LogLevel::getName($i),
];
}
$view->assignMultiple(
[
'levels' => $levels,
'startLevel' => (int)$this->getConfigOption('startLevel'),
'groupByComponent' => $this->getConfigOption('groupByComponent'),
'groupByLevel' => $this->getConfigOption('groupByLevel'),
]
);
return $view->render();
}
/**
* Sub-Module content as rendered HTML
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
* @return string
*/
public function getContent(ModuleData $data): string
{
$this->logLevel = $this->getConfigOption('startLevel');
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Debug/Log.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$sortedLog = [];
// settings for this module
$groupByComponent = $this->getConfigOption('groupByComponent');
$groupByLevel = $this->getConfigOption('groupByLevel');
foreach ($data['log'] as $logRecord) {
if ($logRecord['level'] > $this->logLevel) {
continue;
}
if ($groupByComponent && $groupByLevel) {
$sortedLog[$logRecord['component']][LogLevel::getName($logRecord['level'])][] = $logRecord;
} elseif ($groupByComponent) {
$sortedLog[$logRecord['component']][] = $logRecord;
} elseif ($groupByLevel) {
$sortedLog[$logRecord['level']][] = $logRecord;
} else {
$sortedLog[] = $logRecord;
}
}
$data['log'] = $sortedLog;
$data['groupByComponent'] = $groupByComponent;
$data['groupByLevel'] = $groupByLevel;
$view->assignMultiple($data->getArrayCopy());
return $view->render();
}
/**
* @inheritdoc
*/
public function initializeModule(ServerRequestInterface $request): void
{
$this->logLevel = $this->getConfigOption('startLevel');
// set inMemoryLogWriter recursively for all configured namespaces/areas so we don't lose log entries
$configWithInMemoryWriter = $this->setLoggingConfigRecursive($GLOBALS['TYPO3_CONF_VARS']['LOG'] ?? []);
// in case there are empty array parts, remove them
$GLOBALS['TYPO3_CONF_VARS']['LOG'] = array_filter(
$configWithInMemoryWriter
);
}
protected function setLoggingConfigRecursive(array $logConfig): array
{
foreach ($logConfig as $key => $value) {
if ($key === 'writerConfiguration') {
$logConfig[$key] = $value;
$logConfig[$key][LogLevel::DEBUG][InMemoryLogWriter::class] = [];
} elseif (is_array($value)) {
$logConfig[$key] = $this->setLoggingConfigRecursive($value);
}
}
return $logConfig;
}
/**
* @param string $option
* @return string
*/
protected function getConfigOption(string $option): string
{
return $this->configurationService->getConfigurationOption('debug_log', $option);
}
}
@@ -0,0 +1,131 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\Debug;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\Log\DoctrineSqlLogger;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* Admin Panel Query Information module for showing SQL Queries
*
* @internal
*/
class QueryInformation extends AbstractSubModule implements DataProviderInterface
{
/**
* Identifier for this Sub-module,
* for example "preview" or "cache"
*
* @return string
*/
public function getIdentifier(): string
{
return 'debug_queryinformation';
}
/**
* Sub-Module label
*
* @return string
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:submodule.queryInformation.label'
);
}
/**
* @param ServerRequestInterface $request
* @return \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData
* @throws \Doctrine\DBAL\DBALException
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$connection = $connectionPool->getConnectionByName(ConnectionPool::DEFAULT_CONNECTION_NAME);
$logger = $connection->getConfiguration()->getSQLLogger();
$data = [];
if ($logger instanceof DoctrineSqlLogger) {
$queries = $logger->getQueries();
$data['queries'] = $this->groupQueries($queries) ?? [];
$data['totalTime'] = array_sum(array_column($queries, 'executionMS')) * 1000;
}
return new ModuleData($data);
}
/**
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
* @return string Returns content of admin panel
*/
public function getContent(ModuleData $data): string
{
$view = new StandaloneView();
$view->setTemplatePathAndFilename(
'EXT:adminpanel/Resources/Private/Templates/Modules/Debug/QueryInformation.html'
);
$this->getLanguageService()->includeLLFile('EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf');
$view->assignMultiple($data->getArrayCopy());
return $view->render();
}
/**
* @param array $queries
* @return array
*/
protected function groupQueries(array $queries): array
{
$groupedQueries = [];
foreach ($queries as $query) {
$identifier = sha1($query['sql']) . sha1(json_encode($query['backtrace']));
if (is_array($query['params'])) {
foreach ($query['params'] as $k => $param) {
if (is_array($param)) {
$query['params'][$k] = implode(',', $param);
}
}
}
if (isset($groupedQueries[$identifier])) {
$groupedQueries[$identifier]['count']++;
$groupedQueries[$identifier]['time'] += ($query['executionMS'] * 1000);
$groupedQueries[$identifier]['queries'][] = $query;
} else {
$groupedQueries[$identifier] = [
'sql' => $query['sql'],
'time' => $query['executionMS'] * 1000,
'count' => 1,
'queries' => [
$query,
],
];
}
}
uasort(
$groupedQueries,
function ($a, $b) {
return $b['time'] <=> $a['time'];
}
);
return $groupedQueries;
}
}
@@ -0,0 +1,65 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\Log\InMemoryLogWriter;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\ShortInfoProviderInterface;
use TYPO3\CMS\Core\Log\LogRecord;
/**
* Debug Module of the AdminPanel
*/
class DebugModule extends AbstractModule implements ShortInfoProviderInterface
{
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'debug';
}
/**
* @inheritdoc
*/
public function getIconIdentifier(): string
{
return 'actions-debug';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:module.label'
);
}
public function getShortInfo(): string
{
$errorsAndWarnings = array_filter(InMemoryLogWriter::$log, function (LogRecord $entry) {
return $entry->getLevel() <= 4;
});
return sprintf($this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_debug.xlf:module.shortinfo'
), count($errorsAndWarnings));
}
}
@@ -0,0 +1,153 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\PageSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface;
use TYPO3\CMS\Adminpanel\Service\EditToolbarService;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Admin Panel Edit Module
*/
class EditModule extends AbstractModule implements PageSettingsProviderInterface, InitializableInterface, ResourceProviderInterface
{
/**
* Creates the content for the "edit" section ("module") of the Admin Panel
*
* @return string HTML content for the section. Consists of a string with table-rows with four columns.
*/
public function getPageSettings(): string
{
$editToolbarService = GeneralUtility::makeInstance(EditToolbarService::class);
$toolbar = $editToolbarService->createToolbar();
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Settings/Edit.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple(
[
'feEdit' => ExtensionManagementUtility::isLoaded('feedit'),
'display' => [
'fieldIcons' => $this->configurationService->getConfigurationOption('edit', 'displayFieldIcons'),
'displayIcons' => $this->configurationService->getConfigurationOption('edit', 'displayIcons'),
],
'toolbar' => $toolbar,
'script' => [
'pageUid' => (int)$this->getTypoScriptFrontendController()->page['uid'],
'pageModule' => $this->getPageModule(),
'backendScript' => BackendUtility::getBackendScript(),
't3BeSitenameMd5' => md5('Typo3Backend-' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']),
],
]
);
return $view->render();
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
/**
* @return string
*/
private function getPageModule(): string
{
$pageModule = trim($this->getBackendUser()->getTSConfig()['options.']['overridePageModule'] ?? '');
return BackendUtility::isModuleSetInTBE_MODULES($pageModule) ? $pageModule : 'web_layout';
}
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'edit';
}
/**
* @inheritdoc
*/
public function getIconIdentifier(): string
{
return 'actions-open';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
$locallangFileAndPath = 'LLL:EXT:adminpanel/Resources/Private/Language/locallang_edit.xlf:module.label';
return $this->getLanguageService()->sL($locallangFileAndPath);
}
/**
* Initialize the edit module
* Includes the frontend edit initialization
*
* @param ServerRequestInterface $request
*
* @todo move into fe_edit (including the module)
*/
public function initializeModule(ServerRequestInterface $request): void
{
$extFeEditLoaded = ExtensionManagementUtility::isLoaded('feedit');
$typoScriptFrontend = $this->getTypoScriptFrontendController();
$typoScriptFrontend->displayEditIcons = $this->configurationService->getConfigurationOption('edit', 'displayIcons');
$typoScriptFrontend->displayFieldEditIcons = $this->configurationService->getConfigurationOption('edit', 'displayFieldIcons');
if ($request->getQueryParams()['ADMCMD_editIcons'] ?? $request->getParsedBody()['ADMCMD_editIcons'] ?? false) {
$typoScriptFrontend->displayFieldEditIcons = '1';
}
if ($extFeEditLoaded && $typoScriptFrontend->displayEditIcons) {
$typoScriptFrontend->set_no_cache('Admin Panel: Display edit icons', true);
}
if ($extFeEditLoaded && $typoScriptFrontend->displayFieldEditIcons) {
$typoScriptFrontend->set_no_cache('Admin Panel: Display field edit icons', true);
}
}
/**
* @return array
*/
public function getJavaScriptFiles(): array
{
return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.js'];
}
/**
* Returns a string array with css files that will be rendered after the module
*
* @return array
*/
public function getCssFiles(): array
{
return [];
}
}
@@ -0,0 +1,186 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\Info;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* General information module displaying info about the current
* request
*
* @internal
*/
class GeneralInformation extends AbstractSubModule implements DataProviderInterface
{
/**
* @inheritdoc
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
/** @var UserAspect $frontendUserAspect */
$frontendUserAspect = GeneralUtility::makeInstance(Context::class)->getAspect('frontend.user');
$tsfe = $this->getTypoScriptFrontendController();
return new ModuleData(
[
'post' => $_POST,
'get' => $_GET,
'cookie' => $_COOKIE,
'server' => $_SERVER,
'info' => [
'pageUid' => $tsfe->id,
'pageType' => $tsfe->type,
'groupList' => implode(',', $frontendUserAspect->getGroupIds()),
'noCache' => $this->isNoCacheEnabled(),
'countUserInt' => count($tsfe->config['INTincScript'] ?? []),
'totalParsetime' => $this->getTimeTracker()->getParseTime(),
'feUser' => [
'uid' => $frontendUserAspect->get('id') ?: 0,
'username' => $frontendUserAspect->get('username') ?: '',
],
'imagesOnPage' => $this->collectImagesOnPage(),
'documentSize' => $this->collectDocumentSize(),
],
]
);
}
/**
* Creates the content for the "info" section ("module") of the Admin Panel
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
* @return string HTML content for the section. Consists of a string with table-rows with four columns.
* @see display()
*/
public function getContent(ModuleData $data): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Info/General.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple($data->getArrayCopy());
return $view->render();
}
/**
* Identifier for this Sub-module,
* for example "preview" or "cache"
*
* @return string
*/
public function getIdentifier(): string
{
return 'info_general';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_info.xlf:sub.general.label'
);
}
/**
* Collects images from TypoScriptFrontendController and calculates the total size.
* Returns human readable image sizes for fluid template output
*
* @return array
*/
protected function collectImagesOnPage(): array
{
$imagesOnPage = [
'files' => [],
'total' => 0,
'totalSize' => 0,
'totalSizeHuman' => GeneralUtility::formatSize(0),
];
if ($this->isNoCacheEnabled() === false) {
return $imagesOnPage;
}
$count = 0;
$totalImageSize = 0;
if (!empty($this->getTypoScriptFrontendController()->imagesOnPage)) {
foreach ($this->getTypoScriptFrontendController()->imagesOnPage as $file) {
$fileSize = @filesize($file);
$imagesOnPage['files'][] = [
'name' => $file,
'size' => $fileSize,
'sizeHuman' => GeneralUtility::formatSize($fileSize),
];
$totalImageSize += $fileSize;
$count++;
}
}
$imagesOnPage['totalSize'] = GeneralUtility::formatSize($totalImageSize);
$imagesOnPage['total'] = $count;
return $imagesOnPage;
}
/**
* Gets the document size from the current page in a human readable format
*
* @return string
*/
protected function collectDocumentSize(): string
{
$documentSize = 0;
if ($this->isNoCacheEnabled() === true) {
$documentSize = mb_strlen($this->getTypoScriptFrontendController()->content, 'UTF-8');
}
return GeneralUtility::formatSize($documentSize);
}
/**
* @return bool
*/
protected function isNoCacheEnabled(): bool
{
return (bool)$this->getTypoScriptFrontendController()->no_cache;
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
/**
* @return TimeTracker
*/
protected function getTimeTracker(): TimeTracker
{
return GeneralUtility::makeInstance(TimeTracker::class);
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\Info;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* PhpInformation admin panel sub module
*
* @internal
*/
class PhpInformation extends AbstractSubModule implements DataProviderInterface
{
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'info_php';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_info.xlf:sub.php.label'
);
}
/**
* @inheritdoc
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
return new ModuleData(
[
'general' => [
'PHP_VERSION' => PHP_VERSION,
'PHP_OS' => PHP_OS,
'PHP_SAPI' => PHP_SAPI,
'Peak Memory Usage' => GeneralUtility::formatSize(memory_get_peak_usage()),
],
'loadedExtensions' => implode(', ', get_loaded_extensions()),
'constants' => get_defined_constants(true),
]
);
}
/**
* @inheritdoc
*/
public function getContent(ModuleData $data): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Info/PhpInfo.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple($data->getArrayCopy());
return $view->render();
}
}
@@ -0,0 +1,81 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\Info;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
/**
* RequestInformation submodule of the admin panel
*
* @internal
*/
class RequestInformation extends AbstractSubModule implements DataProviderInterface
{
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'info-request';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_info.xlf:sub.request.label'
);
}
/**
* @inheritdoc
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
return new ModuleData(
[
'post' => $_POST,
'get' => $_GET,
'cookie' => $_COOKIE,
'session' => $_SESSION,
'server' => $_SERVER,
]
);
}
/**
* @inheritdoc
*/
public function getContent(ModuleData $data): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Info/RequestInformation.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple($data->getArrayCopy());
return $view->render();
}
}
@@ -0,0 +1,73 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\ShortInfoProviderInterface;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Admin Panel Info Module
*/
class InfoModule extends AbstractModule implements ShortInfoProviderInterface
{
/**
* @inheritdoc
*/
public function getIconIdentifier(): string
{
return 'actions-document-info';
}
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'info';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_info.xlf:module.label'
);
}
/**
* @inheritdoc
*/
public function getShortInfo(): string
{
$parseTime = $this->getTimeTracker()->getParseTime();
return sprintf($this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_info.xlf:module.shortinfo'
), $parseTime);
}
/**
* @return TimeTracker
*/
protected function getTimeTracker(): TimeTracker
{
return GeneralUtility::makeInstance(TimeTracker::class);
}
}
@@ -0,0 +1,257 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\PageSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface;
use TYPO3\CMS\Adminpanel\Repositories\FrontendGroupsRepository;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\DateTimeAspect;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Context\VisibilityAspect;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Admin Panel Preview Module
*/
class PreviewModule extends AbstractModule implements InitializableInterface, PageSettingsProviderInterface, ResourceProviderInterface
{
/**
* module configuration, set on initialize
*
* @var array
*/
protected $config;
/**
* @inheritdoc
*/
public function getIconIdentifier(): string
{
return 'actions-preview';
}
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'preview';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_preview.xlf:module.label'
);
}
/**
* @inheritdoc
*/
public function initializeModule(ServerRequestInterface $request): void
{
// Backend preview params (ADMCMD_) take precedence over configured admin panel values
$simulateGroupByRequest = (int)($request->getQueryParams()['ADMCMD_simUser'] ?? 0);
$simulateTimeByRequest = (int)($request->getQueryParams()['ADMCMD_simTime'] ?? 0);
$this->config = [
'showHiddenPages' => (bool)$this->getConfigOptionForModule('showHiddenPages'),
'simulateDate' => $simulateTimeByRequest ?: (int)$this->getConfigOptionForModule('simulateDate'),
'showHiddenRecords' => (bool)$this->getConfigOptionForModule('showHiddenRecords'),
'simulateUserGroup' => $simulateGroupByRequest ?: (int)$this->getConfigOptionForModule('simulateUserGroup'),
'showFluidDebug' => (bool)$this->getConfigOptionForModule('showFluidDebug'),
];
if ($this->config['showFluidDebug']) {
// forcibly unset fluid caching as it does not care about the tsfe based caching settings
unset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['fluid_template']['frontend']);
$GLOBALS['TSFE']->set_no_cache('Cache is disabled if fluid debugging is enabled', true);
}
$this->initializeFrontendPreview(
$this->config['showHiddenPages'],
$this->config['showHiddenRecords'],
$this->config['simulateDate'],
$this->config['simulateUserGroup']
);
}
/**
* @inheritdoc
*/
public function getPageSettings(): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/Settings/Preview.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$frontendGroupsRepository = GeneralUtility::makeInstance(FrontendGroupsRepository::class);
$view->assignMultiple(
[
'show' => [
'pageId' => (int)$this->getTypoScriptFrontendController()->id,
'hiddenPages' => $this->config['showHiddenPages'],
'hiddenRecords' => $this->config['showHiddenRecords'],
'fluidDebug' => $this->config['showFluidDebug'],
],
'simulateDate' => $this->config['simulateDate'],
'frontendUserGroups' => [
'availableGroups' => $frontendGroupsRepository->getAvailableFrontendUserGroups(),
'selected' => (int)$this->config['simulateUserGroup'],
],
]
);
return $view->render();
}
/**
* @param string $option
* @return string
*/
protected function getConfigOptionForModule(string $option): string
{
return $this->configurationService->getConfigurationOption(
'preview',
$option
);
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
/**
* Initialize frontend preview functionality incl.
* simulation of users or time
*
* @param bool $showHiddenPages
* @param bool $showHiddenRecords
* @param int $simulateDate
* @param int $simulateUserGroup UID of the fe_group to simulate
* @throws \Exception
*/
protected function initializeFrontendPreview(
bool $showHiddenPages,
bool $showHiddenRecords,
int $simulateDate,
int $simulateUserGroup
): void {
$context = GeneralUtility::makeInstance(Context::class);
$typoScriptFrontendController = $this->getTypoScriptFrontendController();
$typoScriptFrontendController->clear_preview();
$typoScriptFrontendController->fePreview = 1;
// Modify visibility settings (hidden pages + hidden content)
$context->setAspect(
'visibility',
GeneralUtility::makeInstance(VisibilityAspect::class, $showHiddenPages, $showHiddenRecords)
);
// Simulate date
$simTime = null;
if ($simulateDate) {
$simTime = $this->parseDate($simulateDate);
if ($simTime) {
$GLOBALS['SIM_EXEC_TIME'] = $simTime;
$GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
$context->setAspect(
'date',
GeneralUtility::makeInstance(
DateTimeAspect::class,
new \DateTimeImmutable('@' . $GLOBALS['SIM_EXEC_TIME'])
)
);
}
}
// simulate usergroup
if ($simulateUserGroup) {
$typoScriptFrontendController->simUserGroup = $simulateUserGroup;
if (!$typoScriptFrontendController->fe_user instanceof FrontendUserAuthentication) {
$typoScriptFrontendController->fe_user = GeneralUtility::makeInstance(
FrontendUserAuthentication::class
);
}
if (!is_array($typoScriptFrontendController->fe_user->user)) {
// This can be removed once #90989 is fixed
$typoScriptFrontendController->fe_user->user = ['uid' => PHP_INT_MAX];
}
$typoScriptFrontendController->fe_user->user[$typoScriptFrontendController->fe_user->usergroup_column] = $simulateUserGroup;
$context->setAspect(
'frontend.user',
GeneralUtility::makeInstance(
UserAspect::class,
$typoScriptFrontendController->fe_user ?: null,
[$simulateUserGroup]
)
);
}
if (!$simulateUserGroup && !$simTime && !$showHiddenPages && !$showHiddenRecords) {
$typoScriptFrontendController->fePreview = 0;
}
}
/**
* @return array
*/
public function getJavaScriptFiles(): array
{
return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Preview.js'];
}
/**
* The simulated date needs to be a timestring (UTC)
*
* Simulation date is either set via configuration of AdminPanel (Date and Time Fields) or via ADMCMD_ $_GET
* parameter from backend previews
*
* @param int $simulateDate
* @return int
*/
protected function parseDate(int $simulateDate): ?int
{
try {
$simTime = (new \DateTime('@' . $simulateDate))->getTimestamp();
$simTime = max($simTime, 60);
} catch (\Exception $e) {
$simTime = null;
}
return $simTime;
}
/**
* Returns a string array with css files that will be rendered after the module
*
* @return array
*/
public function getCssFiles(): array
{
return [];
}
}
@@ -0,0 +1,193 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules\TsDebug;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\InitializableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleSettingsProviderInterface;
use TYPO3\CMS\Adminpanel\Service\ConfigurationService;
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Class TypoScriptWaterfall
*
* @internal
*/
class TypoScriptWaterfall extends AbstractSubModule implements InitializableInterface, ModuleSettingsProviderInterface
{
/**
* @var ConfigurationService
*/
protected $configurationService;
public function __construct()
{
$this->configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
}
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'typoscript-waterfall';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_tsdebug.xlf:sub.waterfall.label'
);
}
/**
* @inheritdoc
*/
public function initializeModule(ServerRequestInterface $request): void
{
$typoScriptFrontend = $this->getTypoScriptFrontendController();
$typoScriptFrontend->forceTemplateParsing = $this->getConfigurationOption(
'forceTemplateParsing'
);
if ($typoScriptFrontend->forceTemplateParsing) {
$typoScriptFrontend->set_no_cache('Admin Panel: Force template parsing', true);
}
$this->getTimeTracker()->LR = (bool)$this->getConfigurationOption('LR');
}
/**
* Creates the content for the "tsdebug" section ("module") of the Admin Panel
*
* @param ModuleData $data
* @return string HTML
*/
public function getContent(ModuleData $data): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/TsDebug/TypoScript.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple(
[
'tree' => (int)$this->getConfigurationOption('tree'),
'display' => [
'times' => (int)$this->getConfigurationOption('displayTimes'),
'messages' => (int)$this->getConfigurationOption('displayMessages'),
'content' => (int)$this->getConfigurationOption('displayContent'),
],
'trackContentRendering' => (int)$this->getConfigurationOption('LR'),
'forceTemplateParsing' => (int)$this->getConfigurationOption('forceTemplateParsing'),
'typoScriptLog' => $this->renderTypoScriptLog(),
]
);
return $view->render();
}
/**
* @inheritdoc
*/
public function getSettings(): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);
$templateNameAndPath = 'EXT:adminpanel/Resources/Private/Templates/Modules/TsDebug/TypoScriptSettings.html';
$view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templateNameAndPath));
$view->setPartialRootPaths(['EXT:adminpanel/Resources/Private/Partials']);
$view->assignMultiple(
[
'tree' => (int)$this->getConfigurationOption('tree'),
'display' => [
'times' => (int)$this->getConfigurationOption('displayTimes'),
'messages' => (int)$this->getConfigurationOption('displayMessages'),
'content' => (int)$this->getConfigurationOption('displayContent'),
],
'trackContentRendering' => (int)$this->getConfigurationOption('LR'),
'forceTemplateParsing' => (int)$this->getConfigurationOption('forceTemplateParsing')
]
);
return $view->render();
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication|FrontendBackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
/**
* @param string $option
* @return bool
*/
protected function getConfigurationOption(string $option): bool
{
return (bool)$this->configurationService->getConfigurationOption('tsdebug', $option);
}
/**
* Renders the TypoScript log as string
*
* @return string
*/
protected function renderTypoScriptLog(): string
{
$timeTracker = $this->getTimeTracker();
$timeTracker->printConf['flag_tree'] = $this->getConfigurationOption('tree');
$timeTracker->printConf['allTime'] = $this->getConfigurationOption(
'displayTimes'
);
$timeTracker->printConf['flag_messages'] = $this->getConfigurationOption(
'displayMessages'
);
$timeTracker->printConf['flag_content'] = $this->getConfigurationOption(
'displayContent'
);
return $timeTracker->printTSlog();
}
/**
* @return TimeTracker
*/
protected function getTimeTracker(): TimeTracker
{
return GeneralUtility::makeInstance(TimeTracker::class);
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
}
@@ -0,0 +1,79 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Modules;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractModule;
use TYPO3\CMS\Adminpanel\ModuleApi\ShortInfoProviderInterface;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Admin Panel TypoScript Debug Module
*/
class TsDebugModule extends AbstractModule implements ShortInfoProviderInterface
{
/**
* @inheritdoc
*/
public function getIdentifier(): string
{
return 'tsdebug';
}
/**
* @inheritdoc
*/
public function getIconIdentifier(): string
{
return 'mimetypes-x-content-template-static';
}
/**
* @inheritdoc
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_tsdebug.xlf:module.label'
);
}
/**
* @inheritdoc
*/
public function getShortInfo(): string
{
$messageCount = 0;
foreach ($this->getTimeTracker()->tsStackLog as $log) {
$messageCount += count($log['message'] ?? []);
}
return sprintf(
$this->getLanguageService()->sL(
'LLL:EXT:adminpanel/Resources/Private/Language/locallang_tsdebug.xlf:module.shortinfo'
),
$messageCount
);
}
/**
* @return TimeTracker
*/
protected function getTimeTracker(): TimeTracker
{
return GeneralUtility::makeInstance(TimeTracker::class);
}
}
@@ -0,0 +1,121 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Repositories;
/*
* 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!
*/
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Admin Panel Frontend Groups Repository
*
* @internal
*/
class FrontendGroupsRepository
{
/**
* returns an array of all available frontend user groups including hidden ones.
*
* @return array
*/
public function getAvailableFrontendUserGroups(): array
{
$optionCount = $this->getUserGroupOptionCountByBackendUser($this->getBackendUser());
$frontendGroups = [];
if ($optionCount > 0) {
$frontendGroups = $this->getUserGroupsForPagesByBackendUser($this->getBackendUser());
}
return $frontendGroups;
}
/**
* fetches the amount of user groups
*
* @param FrontendBackendUserAuthentication $beUser
* @return int
*/
protected function getUserGroupOptionCountByBackendUser(FrontendBackendUserAuthentication $beUser): int
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('fe_groups');
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$optionCount = $queryBuilder->count('fe_groups.uid')
->from('fe_groups', 'fe_groups')
->innerJoin(
'fe_groups',
'pages',
'pages',
$queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('fe_groups.pid'))
)
->where(
$beUser->getPagePermsClause(Permission::PAGE_SHOW)
)
->execute()
->fetchColumn(0);
return (int)$optionCount;
}
/**
* fetches all frontend user groups, except deleted, for pages
*
* @param FrontendBackendUserAuthentication $beUser
* @return array
*/
protected function getUserGroupsForPagesByBackendUser(FrontendBackendUserAuthentication $beUser): array
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('fe_groups');
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
return $queryBuilder->select('fe_groups.uid', 'fe_groups.title')
->from('fe_groups')
->innerJoin(
'fe_groups',
'pages',
'pages',
$queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('fe_groups.pid'))
)
->where(
$beUser->getPagePermsClause(Permission::PAGE_SHOW)
)
->orderBy('fe_groups.title')
->execute()
->fetchAll();
}
/**
* Returns the current BE user.
*
* @return FrontendBackendUserAuthentication
*/
protected function getBackendUser(): FrontendBackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
@@ -0,0 +1,136 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Service;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ConfigurableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\OnSubmitActorInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\SubmoduleProviderInterface;
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\SingletonInterface;
/**
* Admin Panel Service Class for Configuration Handling
*
* Scope: User TSConfig + Backend User UC
*/
class ConfigurationService implements SingletonInterface
{
/**
* @var array
*/
protected $mainConfiguration;
public function __construct()
{
$this->mainConfiguration = $this->getBackendUser()->getTSConfig()['admPanel.'] ?? [];
}
/**
* Get MainConfiguration (User TSConfig admPanel)
*
* @return array
*/
public function getMainConfiguration(): array
{
return $this->mainConfiguration;
}
/**
* Helper method to return configuration options
* Checks User TSConfig overrides and current backend user session
*
* @param string $identifier
* @param string $option
* @return string
*/
public function getConfigurationOption(string $identifier, string $option): string
{
if ($identifier === '' || $option === '') {
throw new \InvalidArgumentException('Identifier and option may not be empty', 1532861423);
}
if (isset($this->mainConfiguration['override.'][$identifier . '.'][$option])) {
$returnValue = $this->mainConfiguration['override.'][$identifier . '.'][$option];
} else {
$returnValue = $this->getBackendUser()->uc['AdminPanel'][$identifier . '_' . $option] ?? '';
}
return (string)$returnValue;
}
/**
* Save admin panel configuration to backend user UC
* triggers onSubmit method of modules to enable each module
* to enhance the save action
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[] $modules
* @param ServerRequestInterface $request
*/
public function saveConfiguration(array $modules, ServerRequestInterface $request): void
{
$configurationToSave = $request->getParsedBody()['TSFE_ADMIN_PANEL'] ?? [];
$beUser = $this->getBackendUser();
$this->triggerOnSubmitActors($modules, $request, $configurationToSave);
$existingConfiguration = $beUser->uc['AdminPanel'] ?? [];
$existingConfiguration = is_array($existingConfiguration) ? $existingConfiguration : [];
// Settings
$beUser->uc['AdminPanel'] = array_merge($existingConfiguration, $configurationToSave);
unset($beUser->uc['AdminPanel']['action']);
// Saving
$beUser->writeUC();
}
/**
* Returns the current BE user.
*
* @return BackendUserAuthentication|FrontendBackendUserAuthentication
*/
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
/**
* @param array $modules
* @param \Psr\Http\Message\ServerRequestInterface $request
* @param $configurationToSave
*/
protected function triggerOnSubmitActors(
array $modules,
ServerRequestInterface $request,
$configurationToSave
): void {
foreach ($modules as $module) {
if (
$module instanceof OnSubmitActorInterface
&& (
($module instanceof ConfigurableInterface && $module->isEnabled())
|| !($module instanceof ConfigurableInterface)
)
) {
$module->onSubmit($configurationToSave, $request);
}
if ($module instanceof SubmoduleProviderInterface) {
$this->triggerOnSubmitActors($module->getSubModules(), $request, $configurationToSave);
}
}
}
}
@@ -0,0 +1,282 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Service;
/*
* 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!
*/
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\LanguageAspect;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Class for the Edit Toolbar
*
* @internal
*/
class EditToolbarService
{
/**
* Creates the tool bar links for the "edit" section of the Admin Panel.
*
* @return string A string containing images wrapped in <a>-tags linking them to proper functions.
*/
public function createToolbar(): string
{
/** @var LanguageAspect $languageAspect */
$languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$tsfe = $this->getTypoScriptFrontendController();
// If mod.newContentElementWizard.override is set, use that extension's create new content wizard instead:
$moduleName = BackendUtility::getPagesTSconfig($tsfe->page['uid'])['mod.']['newContentElementWizard.']['override'] ?? 'new_content_element';
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$perms = $this->getBackendUser()->calcPerms($tsfe->page);
$langAllowed = $this->getBackendUser()->checkLanguageAccess($languageAspect->getId());
$id = $tsfe->id;
$returnUrl = GeneralUtility::getIndpEnv('REQUEST_URI');
$classes = 'typo3-adminPanel-btn typo3-adminPanel-btn-default';
$output = [];
$output[] = '<div class="typo3-adminPanel-form-group">';
$output[] = ' <div class="typo3-adminPanel-btn-group" role="group">';
// History
$link = (string)$uriBuilder->buildUriFromRoute(
'record_history',
[
'element' => 'pages:' . $id,
'returnUrl' => $returnUrl,
]
);
$title = $this->getLabel('edit_recordHistory');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'#latest" title="' .
$title .
'">';
$output[] = ' ' . $iconFactory->getIcon('actions-document-history-open', Icon::SIZE_SMALL)->render();
$output[] = '</a>';
// New Content
if ($perms & Permission::CONTENT_EDIT && $langAllowed) {
$linkParameters = [
'id' => $id,
'returnUrl' => $returnUrl,
];
if (!empty($languageAspect->getId())) {
$linkParameters['sys_language_uid'] = $languageAspect->getId();
}
$link = (string)$uriBuilder->buildUriFromRoute($moduleName, $linkParameters);
$icon = $iconFactory->getIcon('actions-add', Icon::SIZE_SMALL)->render();
$title = $this->getLabel('edit_newContentElement');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
// Move Page
if ($perms & Permission::PAGE_EDIT) {
$link = (string)$uriBuilder->buildUriFromRoute(
'move_element',
[
'table' => 'pages',
'uid' => $id,
'returnUrl' => $returnUrl,
]
);
$icon = $iconFactory->getIcon('actions-document-move', Icon::SIZE_SMALL)->render();
$title = $this->getLabel('edit_move_page');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
// New Page
if ($perms & Permission::PAGE_NEW) {
$link = (string)$uriBuilder->buildUriFromRoute(
'db_new',
[
'id' => $id,
'pagesOnly' => 1,
'returnUrl' => $returnUrl,
]
);
$icon = $iconFactory->getIcon('actions-page-new', Icon::SIZE_SMALL)->render();
$title = $this->getLabel('edit_newPage');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
// Edit Page
if ($perms & Permission::PAGE_EDIT) {
$link = (string)$uriBuilder->buildUriFromRoute(
'record_edit',
[
'edit[pages][' . $id . ']' => 'edit',
'noView' => 1,
'returnUrl' => $returnUrl,
]
);
$icon = $iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL)->render();
$title = $this->getLabel('edit_editPageProperties');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
// Edit Page Overlay
if ($perms & Permission::PAGE_EDIT && $languageAspect->getId() > 0 && $langAllowed) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('pages');
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
$row = $queryBuilder
->select('uid', 'pid', 't3ver_state')
->from('pages')
->where(
$queryBuilder->expr()->eq(
$GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
$queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)
),
$queryBuilder->expr()->eq(
$GLOBALS['TCA']['pages']['ctrl']['languageField'],
$queryBuilder->createNamedParameter($languageAspect->getId(), \PDO::PARAM_INT)
)
)
->setMaxResults(1)
->execute()
->fetch();
$tsfe->sys_page->versionOL('pages', $row);
if (is_array($row)) {
$link = (string)$uriBuilder->buildUriFromRoute(
'record_edit',
[
'edit[pages][' . $row['uid'] . ']' => 'edit',
'noView' => 1,
'returnUrl' => $returnUrl,
]
);
$icon = $iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL)
->render();
$title = $this->getLabel('edit_editPageOverlay');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
}
// Open list view
if ($this->getBackendUser()->check('modules', 'web_list')) {
$link = (string)$uriBuilder->buildUriFromRoute(
'web_list',
[
'id' => $id,
'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
]
);
$icon = $iconFactory->getIcon('actions-system-list-open', Icon::SIZE_SMALL)->render();
$title = $this->getLabel('edit_db_list');
$output[] = '<a class="' .
$classes .
'" href="' .
htmlspecialchars($link, ENT_QUOTES | ENT_HTML5) .
'" title="' .
$title .
'">';
$output[] = ' ' . $icon;
$output[] = '</a>';
}
$output[] = ' </div>';
$output[] = '</div>';
return implode('', $output);
}
/**
* Translate given key
*
* @param string $key Key for a label in the $LOCAL_LANG array of "sysext/core/Resources/Private/Language/locallang_tsfe.xlf
* @return string The value for the $key
*/
protected function getLabel($key): ?string
{
return htmlspecialchars($this->getLanguageService()->getLL($key), ENT_QUOTES | ENT_HTML5);
}
/**
* @return FrontendBackendUserAuthentication
*/
protected function getBackendUser(): FrontendBackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController(): TypoScriptFrontendController
{
return $GLOBALS['TSFE'];
}
}
@@ -0,0 +1,96 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Service;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\Exceptions\InvalidConfigurationException;
use TYPO3\CMS\Adminpanel\ModuleApi\ConfigurableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\SubmoduleProviderInterface;
use TYPO3\CMS\Core\Service\DependencyOrderingService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Admin Panel Module Loader
*
* @internal
*/
class ModuleLoader
{
/**
* Validates, sorts and initiates the registered modules
*
* @param array $modules
* @return \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[]
* @throws \RuntimeException
*/
public function validateSortAndInitializeModules(array $modules): array
{
if (empty($modules)) {
return [];
}
foreach ($modules as $identifier => $configuration) {
if (empty($configuration) || !is_array($configuration)) {
throw new InvalidConfigurationException(
'Missing configuration for module "' . $identifier . '".',
1519490105
);
}
if (!is_string($configuration['module']) ||
empty($configuration['module']) ||
!class_exists($configuration['module']) ||
!is_subclass_of(
$configuration['module'],
ModuleInterface::class,
true
)
) {
throw new InvalidConfigurationException(
'The module "' .
$identifier .
'" defines an invalid module class. Ensure the class exists and implements the "' .
ModuleInterface::class .
'".',
1519490112
);
}
}
$orderedModules = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
$modules
);
$moduleInstances = [];
foreach ($orderedModules as $moduleConfiguration) {
$module = GeneralUtility::makeInstance($moduleConfiguration['module']);
if (
$module instanceof ModuleInterface
&& (
($module instanceof ConfigurableInterface && $module->isEnabled())
|| !($module instanceof ConfigurableInterface)
)
) {
$moduleInstances[$module->getIdentifier()] = $module;
}
if ($module instanceof SubmoduleProviderInterface) {
$subModuleInstances = $this->validateSortAndInitializeModules($moduleConfiguration['submodules'] ?? []);
$module->setSubModules($subModuleInstances);
}
}
return $moduleInstances;
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Utility;
/*
* 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!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Helper class to keep track of memory consumption in loggers to reduce risk of running out of memory
*
* @internal
*/
class MemoryUtility
{
private const MINIMAL_PERCENT_OF_FREE_MEMORY = 0.30;
/**
* Checks memory usage used in current process - this is no guarantee for not running out of memory
* but should prevent memory exhaustion due to the admin panel in most cases.
* The loggers will stop logging once the amount of free memory falls below the threshold (see
* MINIMAL_PERCENT_OF_FREE_MEMORY const).
*
* @return bool
*/
public static function isMemoryConsumptionTooHigh(): bool
{
$iniLimit = ini_get('memory_limit');
$memoryLimit = $iniLimit === '-1' ? -1 : GeneralUtility::getBytesFromSizeMeasurement($iniLimit);
$freeMemory = $memoryLimit - memory_get_usage(true);
return $memoryLimit > 0 && $freeMemory < (self::MINIMAL_PERCENT_OF_FREE_MEMORY * $memoryLimit);
}
}
@@ -0,0 +1,140 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Utility;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\SubmoduleProviderInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
class ResourceUtility
{
/**
* Get additional resources (css, js) from modules and merge it to
* one array - returns an array of full html tags
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface[] $modules
* @return array
*/
public static function getAdditionalResourcesForModules(array $modules): array
{
$result = [
'js' => '',
'css' => '',
];
foreach ($modules as $module) {
if ($module instanceof ResourceProviderInterface) {
foreach ($module->getJavaScriptFiles() as $file) {
$result['js'] .= static::getJsTag($file);
}
foreach ($module->getCssFiles() as $file) {
$result['css'] .= static::getCssTag($file);
}
}
if ($module instanceof SubmoduleProviderInterface) {
$subResult = self::getAdditionalResourcesForModules($module->getSubModules());
$result['js'] .= $subResult['js'];
$result['css'] .= $subResult['css'];
}
}
return $result;
}
public static function getAdditionalResourcesForModule(ResourceProviderInterface $module): array
{
$result = [
'js' => '',
'css' => '',
];
foreach ($module->getJavaScriptFiles() as $file) {
$result['js'] .= static::getJsTag($file);
}
foreach ($module->getCssFiles() as $file) {
$result['css'] .= static::getCssTag($file);
}
return $result;
}
/**
* Returns a link tag with the admin panel stylesheet
* defined using TBE_STYLES
*
* @return string
*/
protected static function getAdminPanelStylesheet(): string
{
$result = '';
if (!empty($GLOBALS['TBE_STYLES']['stylesheets']['admPanel'])) {
$stylesheet = GeneralUtility::locationHeaderUrl($GLOBALS['TBE_STYLES']['stylesheets']['admPanel']);
$result = '<link rel="stylesheet" type="text/css" href="' .
htmlspecialchars($stylesheet, ENT_QUOTES | ENT_HTML5) . '" />';
}
return $result;
}
/**
* Get a css tag for file - with absolute web path resolving
*
* @param string $cssFileLocation
* @return string
*/
protected static function getCssTag(string $cssFileLocation): string
{
$css = '<link type="text/css" rel="stylesheet" href="' .
htmlspecialchars(
PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName($cssFileLocation)),
ENT_QUOTES | ENT_HTML5
) .
'" media="all" />';
return $css;
}
/**
* Get a script tag for JavaScript with absolute paths
*
* @param string $jsFileLocation
* @return string
*/
protected static function getJsTag(string $jsFileLocation): string
{
$js = '<script type="text/javascript" src="' .
htmlspecialchars(
PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName($jsFileLocation)),
ENT_QUOTES | ENT_HTML5
) .
'"></script>';
return $js;
}
/**
* Return a string with tags for main admin panel resources
*
* @return array
*/
public static function getResources(): array
{
$jsFileLocation = 'EXT:adminpanel/Resources/Public/JavaScript/AdminPanel.js';
$js = ResourceUtility::getJsTag($jsFileLocation);
$cssFileLocation = 'EXT:adminpanel/Resources/Public/Css/adminpanel.css';
$css = ResourceUtility::getCssTag($cssFileLocation);
return [
'css' => $css . ResourceUtility::getAdminPanelStylesheet(),
'js' => $js,
];
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\Utility;
/*
* 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!
*/
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
/**
* Helper class to check if the admin panel is enabled and active from outside
*
* Useful for initialization, checks in early hooks or middleware implementations
*/
class StateUtility
{
/**
* Checks if adminPanel was configured to be shown
*
* @return bool
*/
public static function isActivatedForUser(): bool
{
$beUser = $GLOBALS['BE_USER'] ?? null;
if ($beUser instanceof FrontendBackendUserAuthentication) {
$adminPanelConfiguration = $beUser->getTSConfig()['admPanel.'] ?? [];
// set legacy config
$beUser->extAdminConfig = $adminPanelConfiguration;
if (isset($adminPanelConfiguration['enable.'])) {
// only enabled if at least one module is enabled.
return (bool)array_filter($adminPanelConfiguration['enable.']);
}
}
return false;
}
/**
* Returns true if admin panel was activated
* (switched "on" via GUI)
*
* @return bool
*/
public static function isOpen(): bool
{
$beUser = $GLOBALS['BE_USER'] ?? null;
return (bool)($beUser->uc['AdminPanel']['display_top'] ?? false);
}
public static function isActivatedInTypoScript(): bool
{
return (bool)($GLOBALS['TSFE']->config['config']['admPanel'] ?? false);
}
public static function isHiddenForUser(): bool
{
return (bool)($GLOBALS['BE_USER']->extAdminConfig['hide'] ?? false);
}
}
@@ -0,0 +1,480 @@
<?php
namespace TYPO3\CMS\Adminpanel\View;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\ModuleApi\ConfigurableInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface;
use TYPO3\CMS\Adminpanel\Service\EditToolbarService;
use TYPO3\CMS\Adminpanel\Utility\StateUtility;
use TYPO3\CMS\Backend\FrontendBackendUserAuthentication;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* View class for the admin panel in frontend editing.
*
* @internal
*/
class AdminPanelView
{
/**
* Force preview
*
* @var bool
*/
protected $ext_forcePreview = false;
/**
* Array of adminPanel modules
*
* @var \TYPO3\CMS\Adminpanel\ModuleApi\ModuleInterface[]
*/
protected $modules = [];
/**
* @var array
*/
protected $configuration;
/**
* Setter for injecting new-style modules
*
* @see \TYPO3\CMS\Adminpanel\Controller\MainController::render()
* @param array $modules
* @internal
*/
public function setModules(array $modules): void
{
$this->modules = $modules;
}
/**
* Returns LanguageService
*
* @return \TYPO3\CMS\Core\Localization\LanguageService
*/
protected function getLanguageService()
{
return $GLOBALS['LANG'];
}
/**
* Returns the current BE user.
*
* @return FrontendBackendUserAuthentication
*/
protected function getBackendUser()
{
return $GLOBALS['BE_USER'];
}
/*****************************************************
* Admin Panel: Deprecated API
****************************************************/
/**
* Backwards compatibility method ensuring hook still gets the same content as before
*
* @deprecated since TYPO3 v9 - remove when hook can be removed
* @internal
* @return string
* @throws \UnexpectedValueException
*/
public function callDeprecatedHookObject(): string
{
$moduleContent = '';
if (StateUtility::isOpen()) {
foreach ($this->modules as $module) {
$moduleContent .= $this->getModule($module);
}
foreach (
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_adminpanel.php']['extendAdminPanel']
??
[] as $className
) {
trigger_error(
'The hook $GLOBALS[TYPO3_CONF_VARS][SC_OPTIONS][tslib/class.tslib_adminpanel.php][extendAdminPanel] will be removed in TYPO3 v10.0, register an AdminPanelModule instead.',
E_USER_DEPRECATED
);
$hookObject = GeneralUtility::makeInstance($className);
if (!$hookObject instanceof AdminPanelViewHookInterface) {
throw new \UnexpectedValueException(
$className . ' must implement interface ' . AdminPanelViewHookInterface::class,
1311942539
);
}
$content = $hookObject->extendAdminPanel($moduleContent, $this);
if ($content) {
$moduleContent .= '<div class="typo3-adminPanel-section typo3-adminPanel-section-open">';
$moduleContent .= ' <div class="typo3-adminPanel-section-body">';
$moduleContent .= ' ' . $content;
$moduleContent .= ' </div>';
$moduleContent .= '</div>';
}
}
}
return $moduleContent;
}
/**
* Render a single module with header panel
*
* @deprecated since TYPO3 v9 - only used in deprecated hook call (which triggers the corresponding deprecation error)
* @param ModuleInterface $module
* @return string
*/
protected function getModule(ModuleInterface $module): string
{
$output = [];
if ($module instanceof ConfigurableInterface && $module->isEnabled()) {
$output[] = '<div class="typo3-adminPanel-section typo3-adminPanel-section-open">';
$output[] = ' <div class="typo3-adminPanel-section-title">';
$output[] = ' ' . $this->getSectionOpenerLink($module);
$output[] = ' </div>';
$output[] = '<div class="typo3-adminPanel-section-body">';
$output[] = ' ' . $module->getContent();
$output[] = '</div>';
$output[] = '</div>';
}
if ($module instanceof DataProviderInterface) {
foreach ($module->getJavaScriptFiles() as $javaScriptFile) {
$output[] =
'<script src="' .
PathUtility::getAbsoluteWebPath(GeneralUtility::getFileAbsFileName($javaScriptFile)) .
'"></script>';
}
}
return implode('', $output);
}
/*****************************************************
* Admin Panel Layout Helper functions
****************************************************/
/**
* Wraps a string in a link which will open/close a certain part of the Admin Panel
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Only used in deprecated hook call (which triggers the corresponding deprecation error)
* @param ModuleInterface $module
* @return string
*/
protected function getSectionOpenerLink(ModuleInterface $module): string
{
$identifier = $module->getIdentifier();
$onclick = 'document.TSFE_ADMIN_PANEL_FORM[' .
GeneralUtility::quoteJSvalue('TSFE_ADMIN_PANEL[display_' . $identifier . ']') .
'].value=' .
($this->getBackendUser()->uc['AdminPanel']['display_' . $identifier] ? '0' : '1') .
';document.TSFE_ADMIN_PANEL_FORM.submit();return false;';
$output = [];
$output[] = '<span class="typo3-adminPanel-section-title-identifier"></span>';
$output[] = '<a href="javascript:void(0)" onclick="' . htmlspecialchars($onclick) . '">';
$output[] = ' ' . htmlspecialchars($module->getLabel());
$output[] = '</a>';
$output[] = '<input type="hidden" name="TSFE_ADMIN_PANEL[display_' .
$identifier .
']" value="' .
1 .
'" />';
return implode('', $output);
}
/**
* Creates the tool bar links for the "edit" section of the Admin Panel.
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use EditToolbarService instead or create buttons via fluid
* @return string A string containing images wrapped in <a>-tags linking them to proper functions.
*/
public function ext_makeToolBar(): string
{
trigger_error(
'AdminPanelView::ext_makeToolBar() will be removed in TYPO3 v10.0, use fluid and backend uri builder to create a toolbar.',
E_USER_DEPRECATED
);
$editToolbarService = GeneralUtility::makeInstance(EditToolbarService::class);
return $editToolbarService->createToolbar();
}
/**
* Translate given key
*
* @param string $key Key for a label in the $LOCAL_LANG array of "EXT:core/Resources/Private/Language/locallang_tsfe.xlf
* @param bool $convertWithHtmlspecialchars If TRUE the language-label will be sent through htmlspecialchars
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - only used in deprecated methods
* @return string The value for the $key
*/
protected function extGetLL($key, $convertWithHtmlspecialchars = true)
{
$labelStr = $this->getLanguageService()->getLL($key);
if ($convertWithHtmlspecialchars) {
$labelStr = htmlspecialchars($labelStr);
}
return $labelStr;
}
/**
* Add an additional stylesheet
*
* @return string
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
*/
public function getAdminPanelHeaderData()
{
trigger_error(
'AdminPanelView->getAdminPanelHeaderData() will be removed in TYPO3 v10.0. Implement AdminPanelModules via the new API (see AdminPanelModuleInterface).',
E_USER_DEPRECATED
);
$result = '';
if (!empty($GLOBALS['TBE_STYLES']['stylesheets']['admPanel'])) {
$stylesheet = GeneralUtility::locationHeaderUrl($GLOBALS['TBE_STYLES']['stylesheets']['admPanel']);
$result = '<link rel="stylesheet" type="text/css" href="' .
htmlspecialchars($stylesheet, ENT_QUOTES | ENT_HTML5) . '" />';
}
return $result;
}
/**
* Checks if an Admin Panel section ("module") is available for the user. If so, TRUE is returned.
*
* @param string $key The module key, eg. "edit", "preview", "info" etc.
* @return bool
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
*/
public function isAdminModuleEnabled($key)
{
trigger_error(
'AdminPanelView->isAdminModuleEnabled() will be removed in TYPO3 v10.0. Implement AdminPanelModules via the new API (see AdminPanelModuleInterface).',
E_USER_DEPRECATED
);
$result = false;
// Returns TRUE if the module checked is "preview" and the forcePreview flag is set.
if ($key === 'preview' && $this->ext_forcePreview) {
$result = true;
} elseif (!empty($this->configuration['enable.']['all'])) {
$result = true;
} elseif (!empty($this->configuration['enable.'][$key])) {
$result = true;
}
return $result;
}
/**
* Saves any change in settings made in the Admin Panel.
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
*/
public function saveConfigOptions()
{
trigger_error(
'AdminPanelView->saveConfigOptions() will be removed in TYPO3 v10.0. Implement AdminPanelModules via the new API (see AdminPanelModuleInterface).',
E_USER_DEPRECATED
);
$input = GeneralUtility::_GP('TSFE_ADMIN_PANEL');
$beUser = $this->getBackendUser();
if (is_array($input)) {
// Setting
$beUser->uc['AdminPanel'] = array_merge(
!is_array($beUser->uc['AdminPanel']) ? [] : $beUser->uc['AdminPanel'],
$input
);
unset($beUser->uc['AdminPanel']['action']);
foreach ($this->modules as $module) {
if ($module->isEnabled()) {
// We use TYPO3_REQUEST for compatibility reasons. The object and this method are deprecated anyway, this should be fine.
$module->onSubmit($input, $GLOBALS['TYPO3_REQUEST']);
}
}
// Saving
$beUser->writeUC();
// Flush fluid template cache
$cacheManager = new CacheManager();
$cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
$cacheManager->getCache('fluid_template')->flush();
}
}
/**
* Returns the value for an Admin Panel setting.
*
* @param string $sectionName Module key
* @param string $val Setting key
* @return mixed The setting value
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
*/
public function extGetFeAdminValue($sectionName, $val = '')
{
trigger_error(
'AdminPanelView->extGetFeAdminValue() will be removed in TYPO3 v10.0. Implement AdminPanelModules via the new API (see AdminPanelModuleInterface).',
E_USER_DEPRECATED
);
if (!$this->isAdminModuleEnabled($sectionName)) {
return null;
}
$beUser = $this->getBackendUser();
// Exceptions where the values can be overridden (forced) from backend:
// deprecated
if (
$sectionName === 'edit' && (
$val === 'displayIcons' && $this->configuration['module.']['edit.']['forceDisplayIcons'] ||
$val === 'displayFieldIcons' && $this->configuration['module.']['edit.']['forceDisplayFieldIcons'] ||
$val === 'editNoPopup' && $this->configuration['module.']['edit.']['forceNoPopup']
)
) {
return true;
}
// Override all settings with user TSconfig
if ($val && isset($this->configuration['override.'][$sectionName . '.'][$val])) {
return $this->configuration['override.'][$sectionName . '.'][$val];
}
if (!$val && isset($this->configuration['override.'][$sectionName])) {
return $this->configuration['override.'][$sectionName];
}
$returnValue = $val ? $beUser->uc['AdminPanel'][$sectionName . '_' . $val] : 1;
// Exception for preview
if ($sectionName === 'preview' && $this->ext_forcePreview) {
return !$val ? true : $returnValue;
}
// See if the menu is expanded!
return $this->isAdminModuleOpen($sectionName) ? $returnValue : null;
}
/**
* Enables the force preview option.
*
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - see AdminPanelModule: Preview
*/
public function forcePreview()
{
trigger_error(
'AdminPanelView->forcePreview() will be removed in TYPO3 v10.0. Use new AdminPanel Preview Module instead.',
E_USER_DEPRECATED
);
$this->ext_forcePreview = true;
}
/**
* Returns TRUE if admin panel module is open
*
* @param string $key Module key
* @return bool TRUE, if the admin panel is open for the specified admin panel module key.
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - use new AdminPanel API instead
*/
public function isAdminModuleOpen($key)
{
trigger_error(
'AdminPanelView->isAdminModuleOpen() will be removed in TYPO3 v10.0. Use new AdminPanel API instead.',
E_USER_DEPRECATED
);
return $this->getBackendUser()->uc['AdminPanel']['display_top'] &&
$this->getBackendUser()->uc['AdminPanel']['display_' . $key];
}
/**
* Returns a row (with 4 columns) for content in a section of the Admin Panel.
* It will take $pre as a key to a label to display and $element as the content to put into the forth cell.
*
* @param string $title Key to label
* @param string $content The HTML content for the forth table cell.
* @param string $checkbox The HTML for a checkbox or hidden fields.
* @param string $innerDivClass The Class attribute for the td element.
* @param string $outerDivClass The Class attribute for the tr element.
* @return string HTML table row.
* @see extGetHead()
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - use new AdminPanel API instead
*/
public function extGetItem($title, $content = '', $checkbox = '', $outerDivClass = null, $innerDivClass = null)
{
trigger_error(
'AdminPanelView->extGetItem() will be removed in TYPO3 v10.0. Use new AdminPanel API instead.',
E_USER_DEPRECATED
);
$title = $title ? '<label for="' . htmlspecialchars($title) . '">' . $this->extGetLL($title) . '</label>' : '';
$out = '';
$out .= (string)$outerDivClass ? '<div class="' . htmlspecialchars($outerDivClass) . '">' : '<div>';
$out .= (string)$innerDivClass ? '<div class="' . htmlspecialchars($innerDivClass) . '">' : '<div>';
$out .= $checkbox . $title . $content . '</div></div>';
return $out;
}
/**
* Returns a row (with colspan=4) which is a header for a section in the Admin Panel.
* It will have a plus/minus icon and a label which is linked so that it submits the form which surrounds the whole Admin Panel when clicked, alterting the TSFE_ADMIN_PANEL[display_' . $pre . '] value
* See the functions get*Module
*
* @param string $sectionSuffix The suffix to the display_ label. Also selects the label from the LOCAL_LANG array.
* @return string HTML table row.
* @see extGetItem()
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - use new AdminPanel API instead
*/
public function extGetHead($sectionSuffix)
{
trigger_error(
'AdminPanelView->extGetHead() will be removed in TYPO3 v10.0. Use new AdminPanel API instead.',
E_USER_DEPRECATED
);
return $this->linkSectionHeader($sectionSuffix, $this->extGetLL($sectionSuffix));
}
/**
* Wraps a string in a link which will open/close a certain part of the Admin Panel
*
* @param string $sectionSuffix The code for the display_ label/key
* @param string $sectionTitle Title (HTML-escaped)
* @param string $className The classname for the <a> tag
* @return string $className Linked input string
* @see extGetHead()
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0 - use new AdminPanel API instead
*/
public function linkSectionHeader($sectionSuffix, $sectionTitle, $className = '')
{
trigger_error(
'AdminPanelView->linkSectionHeader() will be removed in TYPO3 v10.0. Use new AdminPanel API instead.',
E_USER_DEPRECATED
);
$onclick = 'document.TSFE_ADMIN_PANEL_FORM[' .
GeneralUtility::quoteJSvalue('TSFE_ADMIN_PANEL[display_' . $sectionSuffix . ']') .
'].value=' .
($this->getBackendUser()->uc['AdminPanel']['display_' . $sectionSuffix] ? '0' : '1') .
';document.TSFE_ADMIN_PANEL_FORM.submit();return false;';
$output = [];
$output[] = '<span class="typo3-adminPanel-section-title-identifier"></span>';
$output[] = '<a href="javascript:void(0)" onclick="' . htmlspecialchars($onclick) . '">';
$output[] = ' ' . $sectionTitle;
$output[] = '</a>';
$output[] = '<input type="hidden" name="TSFE_ADMIN_PANEL[display_' .
$sectionSuffix .
']" value="' .
(int)$this->isAdminModuleOpen($sectionSuffix) .
'" />';
return implode('', $output);
}
}
@@ -0,0 +1,32 @@
<?php
namespace TYPO3\CMS\Adminpanel\View;
/*
* 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!
*/
/**
* Interface for classes which hook into AdminPanelView
*
* @deprecated since TYPO3 v9 will be removed in TYPO3 v10.0 - see AdminPanelModuleInterface
*/
interface AdminPanelViewHookInterface
{
/**
* Extend the adminPanel
*
* @param string $moduleContent Content of the admin panel
* @param \TYPO3\CMS\Adminpanel\View\AdminPanelView $obj The adminPanel object
* @return string Returns content of admin panel
*/
public function extendAdminPanel($moduleContent, AdminPanelView $obj);
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ViewHelpers;
/*
* 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!
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* Checks whether the given value is an array
*
* @internal
*/
class IsArrayViewHelper extends AbstractViewHelper
{
public function initializeArguments()
{
$this->registerArgument('value', 'mixed', 'The variable being checked', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return bool
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): bool {
return is_array($arguments['value']);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ViewHelpers;
/*
* 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!
*/
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Get speaking log level from constant
*
* @internal
*/
class SpeakingLogLevelViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* First level cache of user names
*
* @var array
*/
protected static $usernameRuntimeCache = [];
/**
* Initializes the arguments
*/
public function initializeArguments(): void
{
$this->registerArgument('level', 'int', 'Log Level', true);
}
/**
* Resolve user name from backend user id.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string Username or an empty string if there is no user with that UID
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): string {
return LogLevel::getName($arguments['level']);
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Adminpanel\ViewHelpers;
/*
* 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!
*/
use TYPO3\CMS\Adminpanel\ModuleApi\ContentProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleDataStorageCollection;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Render submodule content
*
* @internal
*/
class SubModuleRenderViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* First level cache of user names
*
* @var array
*/
protected static $usernameRuntimeCache = [];
/**
* Initializes the arguments
*/
public function initializeArguments(): void
{
$this->registerArgument(
'module',
ContentProviderInterface::class,
'SubModule instance to be rendered',
true
);
$this->registerArgument('data', ModuleDataStorageCollection::class, 'Data to be used for rendering', true);
}
/**
* Resolve user name from backend user id.
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string Username or an empty string if there is no user with that UID
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
): string {
$module = $arguments['module'];
/** @var \TYPO3\CMS\Adminpanel\ModuleApi\ModuleDataStorageCollection $data */
$data = $arguments['data'];
$moduleData = $data->contains($module) ? $data->offsetGet($module) : new ModuleData();
return $module->getContent($moduleData);
}
}