first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
|
||||
/**
|
||||
* Controller abstract for shared parts of the install tool
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class AbstractController
|
||||
{
|
||||
/**
|
||||
* Helper method to initialize a standalone view instance.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param string $templatePath
|
||||
* @return StandaloneView
|
||||
* @internal param string $template
|
||||
*/
|
||||
protected function initializeStandaloneView(ServerRequestInterface $request, string $templatePath): StandaloneView
|
||||
{
|
||||
$viewRootPath = GeneralUtility::getFileAbsFileName('EXT:install/Resources/Private/');
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->getRequest()->setControllerExtensionName('Install');
|
||||
$view->setTemplatePathAndFilename($viewRootPath . 'Templates/' . $templatePath);
|
||||
$view->setLayoutRootPaths([$viewRootPath . 'Layouts/']);
|
||||
$view->setPartialRootPaths([$viewRootPath . 'Partials/']);
|
||||
$view->assignMultiple([
|
||||
'controller' => $request->getQueryParams()['install']['controller'] ?? 'maintenance',
|
||||
'context' => $request->getQueryParams()['install']['context'] ?? '',
|
||||
'composerMode' => Environment::isComposerMode(),
|
||||
]);
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some actions like the database analyzer and the upgrade wizards need additional
|
||||
* bootstrap actions performed.
|
||||
*
|
||||
* Those actions can potentially fatal if some old extension is loaded that triggers
|
||||
* a fatal in ext_localconf or ext_tables code! Use only if really needed.
|
||||
*/
|
||||
protected function loadExtLocalconfDatabaseAndExtTables()
|
||||
{
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::loadTypo3LoadedExtAndExtLocalconf(false);
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::unsetReservedGlobalVariables();
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::loadBaseTca(false);
|
||||
\TYPO3\CMS\Core\Core\Bootstrap::loadExtTables(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,336 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Core\Authentication\AbstractAuthenticationService;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\InvalidPasswordHashException;
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Http\RedirectResponse;
|
||||
use TYPO3\CMS\Core\Http\Uri;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Fluid\View\StandaloneView;
|
||||
use TYPO3\CMS\Install\Service\SessionService;
|
||||
use TYPO3\CMS\Rsaauth\RsaEncryptionEncoder;
|
||||
|
||||
/**
|
||||
* Backend module controller to the Install Tool. Sets an Install Tool session
|
||||
* marked as "initialized by a valid system administrator backend user" and
|
||||
* redirects to the Install Tool entry point.
|
||||
*
|
||||
* This is a classic backend module that does not interfere with other code
|
||||
* within the Install Tool, it can be seen as a facade around Install Tool just
|
||||
* to embed the Install Tool in backend.
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class BackendModuleController
|
||||
{
|
||||
private const FLAG_CONFIRMATION_REQUEST = 1;
|
||||
protected const FLAG_INSTALL_TOOL_PASSWORD = 2;
|
||||
private const ALLOWED_ACTIONS = ['maintenance', 'settings', 'upgrade', 'environment'];
|
||||
|
||||
/**
|
||||
* @var SessionService
|
||||
*/
|
||||
protected $sessionService;
|
||||
|
||||
/**
|
||||
* @var UriBuilder
|
||||
*/
|
||||
protected $uriBuilder;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows and handles backend user session confirmation ("sudo mode") for
|
||||
* accessing a particular Install Tool controller (as given in `$targetController`).
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function backendUserConfirmationAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$flags = (int)($request->getQueryParams()['flags'] ?? 0);
|
||||
$targetController = (string)($request->getQueryParams()['targetController'] ?? '');
|
||||
$targetHash = (string)($request->getQueryParams()['targetHash'] ?? '');
|
||||
$expectedTargetHash = GeneralUtility::hmac($targetController, BackendModuleController::class);
|
||||
$flagInstallToolPassword = (bool)($flags & self::FLAG_INSTALL_TOOL_PASSWORD);
|
||||
$flagInvalidPassword = false;
|
||||
|
||||
if (!in_array($targetController, self::ALLOWED_ACTIONS, true)
|
||||
|| !hash_equals($expectedTargetHash, $targetHash)) {
|
||||
return new HtmlResponse('', 403);
|
||||
}
|
||||
if ($flags & self::FLAG_CONFIRMATION_REQUEST) {
|
||||
if ($flagInstallToolPassword && $this->verifyInstallToolPassword($request)) {
|
||||
return $this->setAuthorizedAndRedirect($targetController);
|
||||
}
|
||||
if (!$flagInstallToolPassword && $this->verifyBackendUserPassword($request)) {
|
||||
return $this->setAuthorizedAndRedirect($targetController);
|
||||
}
|
||||
$flagInvalidPassword = true;
|
||||
}
|
||||
|
||||
$view = GeneralUtility::makeInstance(StandaloneView::class);
|
||||
$view->getTemplatePaths()->setTemplatePathAndFilename(
|
||||
ExtensionManagementUtility::extPath(
|
||||
'install',
|
||||
'Resources/Private/Templates/BackendModule/BackendUserConfirmation.html'
|
||||
)
|
||||
);
|
||||
$view->assignMultiple([
|
||||
'flagInvalidPassword' => $flagInvalidPassword,
|
||||
'flagInstallToolPassword' => $flagInstallToolPassword,
|
||||
'languageFileReference' => 'LLL:EXT:install/Resources/Private/Language/BackendModule.xlf',
|
||||
'passwordModeUri' => $this->getBackendUserConfirmationUri([
|
||||
'targetController' => $targetController,
|
||||
'targetHash' => $targetHash,
|
||||
// current flags, unset FLAG_CONFIRMATION_REQUEST, toggle FLAG_INSTALL_TOOL_PASSWORD
|
||||
'flags' => $flags & ~self::FLAG_CONFIRMATION_REQUEST ^ self::FLAG_INSTALL_TOOL_PASSWORD,
|
||||
]),
|
||||
'verifyUri' => $this->getBackendUserConfirmationUri([
|
||||
'targetController' => $targetController,
|
||||
'targetHash' => $targetHash,
|
||||
// current flags, add FLAG_CONFIRMATION_REQUEST
|
||||
'flags' => $flags | self::FLAG_CONFIRMATION_REQUEST,
|
||||
]),
|
||||
'cancelUri' => '',
|
||||
]);
|
||||
$this->applyRsaAuthModules();
|
||||
return new HtmlResponse($view->render());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session and redirect to "maintenance"
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function maintenanceAction(): ResponseInterface
|
||||
{
|
||||
return $this->getBackendUserConfirmationRedirect('maintenance')
|
||||
?? $this->setAuthorizedAndRedirect('maintenance');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session and redirect to "settings"
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function settingsAction(): ResponseInterface
|
||||
{
|
||||
return $this->getBackendUserConfirmationRedirect('settings')
|
||||
?? $this->setAuthorizedAndRedirect('settings');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session and redirect to "upgrade"
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function upgradeAction(): ResponseInterface
|
||||
{
|
||||
return $this->getBackendUserConfirmationRedirect('upgrade')
|
||||
?? $this->setAuthorizedAndRedirect('upgrade');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize session and redirect to "environment"
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function environmentAction(): ResponseInterface
|
||||
{
|
||||
return $this->getBackendUserConfirmationRedirect('environment')
|
||||
?? $this->setAuthorizedAndRedirect('environment');
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates redirect response to backend user confirmation (if required).
|
||||
*
|
||||
* @param string $targetController
|
||||
* @return ResponseInterface|null
|
||||
*/
|
||||
protected function getBackendUserConfirmationRedirect(string $targetController): ?ResponseInterface
|
||||
{
|
||||
if ($this->getSessionService()->isAuthorizedBackendUserSession()) {
|
||||
return null;
|
||||
}
|
||||
if (Environment::getContext()->isDevelopment()) {
|
||||
return null;
|
||||
}
|
||||
$redirectUri = $this->getBackendUserConfirmationUri([
|
||||
'targetController' => $targetController,
|
||||
'targetHash' => GeneralUtility::hmac($targetController, BackendModuleController::class),
|
||||
]);
|
||||
return new RedirectResponse((string)$redirectUri, 403);
|
||||
}
|
||||
|
||||
protected function getBackendUserConfirmationUri(array $parameters): Uri
|
||||
{
|
||||
return $this->uriBuilder->buildUriFromRoute(
|
||||
'install.backend-user-confirmation',
|
||||
$parameters
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts / updates the session and redirects to the Install Tool
|
||||
* with given action.
|
||||
*
|
||||
* @param $controller
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
protected function setAuthorizedAndRedirect(string $controller): ResponseInterface
|
||||
{
|
||||
$this->getSessionService()->setAuthorizedBackendSession();
|
||||
$redirectLocation = 'install.php?install[controller]=' . $controller . '&install[context]=backend';
|
||||
return new RedirectResponse($redirectLocation, 303);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that provided password matches Install Tool password.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function verifyInstallToolPassword(ServerRequestInterface $request): bool
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$password = $parsedBody['confirmationPassword'] ?? null;
|
||||
$installToolPassword = $GLOBALS['TYPO3_CONF_VARS']['BE']['installToolPassword'] ?? null;
|
||||
if (!is_string($password) || empty($installToolPassword)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$hashFactory = GeneralUtility::makeInstance(PasswordHashFactory::class);
|
||||
$hashInstance = $hashFactory->get($installToolPassword, 'BE');
|
||||
return $hashInstance->checkPassword($password, $installToolPassword);
|
||||
} catch (InvalidPasswordHashException $exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that provided password is actually correct for current backend user
|
||||
* by stepping through authentication chain in `$GLOBALS['BE_USER]`.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return bool
|
||||
*/
|
||||
protected function verifyBackendUserPassword(ServerRequestInterface $request): bool
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$password = empty($parsedBody['confirmationPasswordInternal'])
|
||||
? ($parsedBody['confirmationPassword'] ?? null) // default field
|
||||
: $parsedBody['confirmationPasswordInternal']; // filled e.g. by `ext:rsaauth`
|
||||
if (!is_string($password)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// clone current backend user object to avoid
|
||||
// possible side effects for the real instance
|
||||
$backendUser = clone $this->getBackendUser();
|
||||
$loginData = [
|
||||
'status' => 'sudo-mode',
|
||||
'origin' => BackendModuleController::class,
|
||||
'uname' => $backendUser->user['username'],
|
||||
'uident' => $password,
|
||||
];
|
||||
// currently there is no dedicated API to perform authentication
|
||||
// that's why this process partially has to be simulated here
|
||||
$loginData = $backendUser->processLoginData($loginData);
|
||||
$authInfo = $backendUser->getAuthInfoArray();
|
||||
|
||||
$authenticated = false;
|
||||
/** @var AbstractAuthenticationService $service or any other service (sic!) */
|
||||
foreach ($this->getAuthServices($backendUser, $loginData, $authInfo) as $service) {
|
||||
$ret = (int)$service->authUser($backendUser->user);
|
||||
if ($ret <= 0) {
|
||||
return false;
|
||||
}
|
||||
if ($ret >= 200) {
|
||||
return true;
|
||||
}
|
||||
if ($ret >= 100) {
|
||||
continue;
|
||||
}
|
||||
$authenticated = true;
|
||||
continue;
|
||||
}
|
||||
return $authenticated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes authentication services to be used in a foreach loop
|
||||
*
|
||||
* @param BackendUserAuthentication $backendUser
|
||||
* @param array $loginData
|
||||
* @param array $authInfo
|
||||
* @return \Generator<int, object>
|
||||
*/
|
||||
protected function getAuthServices(BackendUserAuthentication $backendUser, array $loginData, array $authInfo): \Generator
|
||||
{
|
||||
$serviceChain = [];
|
||||
$subType = 'authUserBE';
|
||||
while ($service = GeneralUtility::makeInstanceService('auth', $subType, $serviceChain)) {
|
||||
$serviceChain[] = $service->getServiceKey();
|
||||
if (!is_object($service)) {
|
||||
break;
|
||||
}
|
||||
$service->initAuth($subType, $loginData, $authInfo, $backendUser);
|
||||
yield $service;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Install Tool modified sessions meta-data (handler, storage, name) which
|
||||
* conflicts with existing session that for instance is used for ext:rsaauth.
|
||||
*
|
||||
* @return SessionService
|
||||
*/
|
||||
protected function getSessionService(): SessionService
|
||||
{
|
||||
if ($this->sessionService === null) {
|
||||
$this->sessionService = new SessionService();
|
||||
$this->sessionService->startSession();
|
||||
}
|
||||
return $this->sessionService;
|
||||
}
|
||||
|
||||
protected function applyRsaAuthModules()
|
||||
{
|
||||
if (!ExtensionManagementUtility::isLoaded('rsaauth')) {
|
||||
return;
|
||||
}
|
||||
$rsaEncryptionEncoder = GeneralUtility::makeInstance(RsaEncryptionEncoder::class);
|
||||
$rsaEncryptionEncoder->enableRsaEncryption(true);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconRegistry;
|
||||
use TYPO3\CMS\Core\Type\Icon\IconState;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Controller for icon handling
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class IconController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var IconRegistry
|
||||
*/
|
||||
protected $iconRegistry;
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/**
|
||||
* Set up dependencies
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
|
||||
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
* @internal
|
||||
*/
|
||||
public function getCacheIdentifierAction(): ResponseInterface
|
||||
{
|
||||
return new HtmlResponse($this->iconRegistry->getCacheIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @internal
|
||||
*/
|
||||
public function getIconAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
$requestedIcon = json_decode($parsedBody['icon'] ?? $queryParams['icon'], true);
|
||||
|
||||
list($identifier, $size, $overlayIdentifier, $iconState, $alternativeMarkupIdentifier) = $requestedIcon;
|
||||
|
||||
if (empty($overlayIdentifier)) {
|
||||
$overlayIdentifier = null;
|
||||
}
|
||||
|
||||
$iconState = IconState::cast($iconState);
|
||||
$icon = $this->iconFactory->getIcon($identifier, $size, $overlayIdentifier, $iconState);
|
||||
|
||||
return new HtmlResponse($icon->render($alternativeMarkupIdentifier));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Http\HtmlResponse;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
|
||||
use TYPO3\CMS\Install\Service\Exception\ConfigurationChangedException;
|
||||
use TYPO3\CMS\Install\Service\SilentConfigurationUpgradeService;
|
||||
|
||||
/**
|
||||
* Layout controller
|
||||
*
|
||||
* Renders a first "load the Javascript in <head>" view, and the
|
||||
* main layout of the install tool in second action.
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class LayoutController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* The init action renders an HTML response with HTML view having <head> section
|
||||
* containing resources to main .js routing.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function initAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Layout/Init.html');
|
||||
$view->assignMultiple([
|
||||
// time is used as cache bust for js and css resources
|
||||
'time' => time(),
|
||||
'siteName' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
|
||||
]);
|
||||
return new HtmlResponse(
|
||||
$view->render(),
|
||||
200,
|
||||
[
|
||||
'Cache-Control' => 'no-cache, must-revalidate',
|
||||
'Pragma' => 'no-cache'
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a json response with the main HTML layout body: Toolbar, main menu and
|
||||
* doc header in standalone, doc header only in backend context. Silent updaters
|
||||
* are executed before this main view is loaded.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function mainLayoutAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Layout/MainLayout.html');
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute silent configuration update. May be called multiple times until success = true is returned.
|
||||
*
|
||||
* @return ResponseInterface success = true if no change has been done
|
||||
*/
|
||||
public function executeSilentConfigurationUpdateAction(): ResponseInterface
|
||||
{
|
||||
$silentUpdate = new SilentConfigurationUpgradeService();
|
||||
$success = true;
|
||||
try {
|
||||
$silentUpdate->execute();
|
||||
} catch (ConfigurationChangedException $e) {
|
||||
$success = false;
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => $success,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy ajax call. This silent updater takes care that all extensions configured in LocalConfiguration
|
||||
* EXT/extConf serialized array are "upmerged" to arrays within EXTENSIONS if this extension does not
|
||||
* exist in EXTENSIONS yet.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3 v10.0
|
||||
*/
|
||||
public function executeSilentLegacyExtConfExtensionConfigurationUpdateAction(): ResponseInterface
|
||||
{
|
||||
$configurationManager = new ConfigurationManager();
|
||||
try {
|
||||
$oldExtConfSettings = $configurationManager->getConfigurationValueByPath('EXT/extConf');
|
||||
} catch (MissingArrayPathException $e) {
|
||||
// The old 'extConf' array may not exist anymore, set to empty array if so.
|
||||
$oldExtConfSettings = [];
|
||||
}
|
||||
try {
|
||||
$newExtensionSettings = $configurationManager->getConfigurationValueByPath('EXTENSIONS');
|
||||
} catch (MissingArrayPathException $e) {
|
||||
// New 'EXTENSIONS' array may not exist yet, for instance if just upgrading to v9
|
||||
$newExtensionSettings = [];
|
||||
}
|
||||
foreach ($oldExtConfSettings as $extensionName => $extensionSettings) {
|
||||
if (!array_key_exists($extensionName, $newExtensionSettings)) {
|
||||
$unserializedConfiguration = unserialize($extensionSettings, ['allowed_classes' => false]);
|
||||
if (is_array($unserializedConfiguration)) {
|
||||
$newExtensionSettings = $this->removeDotsFromArrayKeysRecursive($unserializedConfiguration);
|
||||
$configurationManager->setLocalConfigurationValueByPath('EXTENSIONS/' . $extensionName, $newExtensionSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize TYPO3_CONF_VARS['EXTENSIONS'] with possibly new defaults from extensions
|
||||
* ext_conf_template.txt files. This make LocalConfiguration the only source of truth for
|
||||
* extension configuration and it is always up to date, also if an extension has been
|
||||
* updated.
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function executeSilentExtensionConfigurationSynchronizationAction(): ResponseInterface
|
||||
{
|
||||
$extensionConfiguration = new ExtensionConfiguration();
|
||||
$extensionConfiguration->synchronizeExtConfTemplateWithLocalConfigurationOfAllExtensions();
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for executeSilentLegacyExtConfExtensionConfigurationUpdateAction(). Old EXT/extConf
|
||||
* settings have dots at the end of array keys if nested arrays were used. The new configuration does
|
||||
* not use this funny nested representation anymore. The method removes all dots at the end of given
|
||||
* array keys recursive to do this transition.
|
||||
*
|
||||
* @param array $settings
|
||||
* @return array New settings
|
||||
* @deprecated since TYPO3 v9, will be removed with TYPO3 v10.0 along with executeSilentLegacyExtConfExtensionConfigurationUpdateAction()
|
||||
*/
|
||||
private function removeDotsFromArrayKeysRecursive(array $settings): array
|
||||
{
|
||||
$settingsWithoutDots = [];
|
||||
foreach ($settings as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$settingsWithoutDots[rtrim($key, '.')] = $this->removeDotsFromArrayKeysRecursive($value);
|
||||
} else {
|
||||
$settingsWithoutDots[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $settingsWithoutDots;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
|
||||
use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Install\Service\EnableFileService;
|
||||
|
||||
/**
|
||||
* Login controller
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class LoginController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* Render the "Create an "enable install tool file" action
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function showEnableInstallToolFileAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Login/ShowEnableInstallToolFile.html');
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render login view
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function showLoginAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view = $this->initializeStandaloneView($request, 'Login/ShowLogin.html');
|
||||
$view->assignMultiple([
|
||||
'siteName' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
|
||||
'loginToken' => $formProtection->generateToken('installTool', 'login'),
|
||||
'installToolEnableFilePermanent' => EnableFileService::isInstallToolEnableFilePermanent(),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,770 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
|
||||
use TYPO3\CMS\Core\Core\ClassLoadingInformation;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Schema\Exception\StatementException;
|
||||
use TYPO3\CMS\Core\Database\Schema\SchemaMigrator;
|
||||
use TYPO3\CMS\Core\Database\Schema\SqlReader;
|
||||
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
|
||||
use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Core\Localization\Locales;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
use TYPO3\CMS\Core\Service\OpcodeCacheService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Install\Service\ClearCacheService;
|
||||
use TYPO3\CMS\Install\Service\ClearTableService;
|
||||
use TYPO3\CMS\Install\Service\LanguagePackService;
|
||||
use TYPO3\CMS\Install\Service\Typo3tempFileService;
|
||||
|
||||
/**
|
||||
* Maintenance controller
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class MaintenanceController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* Main "show the cards" view
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function cardsAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/Cards.html');
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache framework and opcode caches
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function cacheClearAllAction(): ResponseInterface
|
||||
{
|
||||
GeneralUtility::makeInstance(ClearCacheService::class)->clearAll();
|
||||
GeneralUtility::makeInstance(OpcodeCacheService::class)->clearAllActive();
|
||||
$messageQueue = (new FlashMessageQueue('install'))->enqueue(
|
||||
new FlashMessage('Successfully cleared all caches and all available opcode caches.', 'Caches cleared')
|
||||
);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear typo3temp files statistics action
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function clearTypo3tempFilesStatsAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/ClearTypo3tempFiles.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'clearTypo3tempFilesToken' => $formProtection->generateToken('installTool', 'clearTypo3tempFiles'),
|
||||
]);
|
||||
return new JsonResponse(
|
||||
[
|
||||
'success' => true,
|
||||
'stats' => (new Typo3tempFileService())->getDirectoryStatistics(),
|
||||
'html' => $view->render(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear typo3temp/assets or FAL processed Files
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function clearTypo3tempFilesAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$typo3tempFileService = new Typo3tempFileService();
|
||||
$folder = $request->getParsedBody()['install']['folder'];
|
||||
// storageUid is an optional post param if FAL storages should be cleaned
|
||||
$storageUid = $request->getParsedBody()['install']['storageUid'] ?? null;
|
||||
if ($storageUid === null) {
|
||||
$typo3tempFileService->clearAssetsFolder($folder);
|
||||
$messageQueue->enqueue(new FlashMessage('Cleared files in "' . $folder . '" folder'));
|
||||
} else {
|
||||
$storageUid = (int)$storageUid;
|
||||
$failedDeletions = $typo3tempFileService->clearProcessedFiles($storageUid);
|
||||
if ($failedDeletions) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Failed to delete ' . $failedDeletions . ' processed files. See TYPO3 log (by default typo3temp/var/log/typo3_*.log)',
|
||||
'',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} else {
|
||||
$messageQueue->enqueue(new FlashMessage('Cleared processed files'));
|
||||
}
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump autoload information
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function dumpAutoloadAction(): ResponseInterface
|
||||
{
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
if (Environment::isComposerMode()) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Skipped generating additional class loading information in composer mode.',
|
||||
'',
|
||||
FlashMessage::NOTICE
|
||||
));
|
||||
} else {
|
||||
ClassLoadingInformation::dumpClassLoadingInformation();
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Successfully dumped class loading information for extensions.'
|
||||
));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get main database analyzer modal HTML
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function databaseAnalyzerAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/DatabaseAnalyzer.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'databaseAnalyzerExecuteToken' => $formProtection->generateToken('installTool', 'databaseAnalyzerExecute'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze current database situation
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function databaseAnalyzerAnalyzeAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$suggestions = [];
|
||||
try {
|
||||
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
|
||||
$sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
|
||||
$schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
|
||||
$addCreateChange = $schemaMigrationService->getUpdateSuggestions($sqlStatements);
|
||||
|
||||
// Aggregate the per-connection statements into one flat array
|
||||
$addCreateChange = array_merge_recursive(...array_values($addCreateChange));
|
||||
if (!empty($addCreateChange['create_table'])) {
|
||||
$suggestion = [
|
||||
'key' => 'addTable',
|
||||
'label' => 'Add tables',
|
||||
'enabled' => true,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($addCreateChange['create_table'] as $hash => $statement) {
|
||||
$suggestion['children'][] = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
if (!empty($addCreateChange['add'])) {
|
||||
$suggestion = [
|
||||
'key' => 'addField',
|
||||
'label' => 'Add fields to tables',
|
||||
'enabled' => true,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($addCreateChange['add'] as $hash => $statement) {
|
||||
$suggestion['children'][] = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
if (!empty($addCreateChange['change'])) {
|
||||
$suggestion = [
|
||||
'key' => 'change',
|
||||
'label' => 'Change fields',
|
||||
'enabled' => false,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($addCreateChange['change'] as $hash => $statement) {
|
||||
$child = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
if (isset($addCreateChange['change_currentValue'][$hash])) {
|
||||
$child['current'] = $addCreateChange['change_currentValue'][$hash];
|
||||
}
|
||||
$suggestion['children'][] = $child;
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
|
||||
// Difference from current to expected
|
||||
$dropRename = $schemaMigrationService->getUpdateSuggestions($sqlStatements, true);
|
||||
|
||||
// Aggregate the per-connection statements into one flat array
|
||||
$dropRename = array_merge_recursive(...array_values($dropRename));
|
||||
if (!empty($dropRename['change_table'])) {
|
||||
$suggestion = [
|
||||
'key' => 'renameTableToUnused',
|
||||
'label' => 'Remove tables (rename with prefix)',
|
||||
'enabled' => false,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($dropRename['change_table'] as $hash => $statement) {
|
||||
$child = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
if (!empty($dropRename['tables_count'][$hash])) {
|
||||
$child['rowCount'] = $dropRename['tables_count'][$hash];
|
||||
}
|
||||
$suggestion['children'][] = $child;
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
if (!empty($dropRename['change'])) {
|
||||
$suggestion = [
|
||||
'key' => 'renameTableFieldToUnused',
|
||||
'label' => 'Remove unused fields (rename with prefix)',
|
||||
'enabled' => false,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($dropRename['change'] as $hash => $statement) {
|
||||
$suggestion['children'][] = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
if (!empty($dropRename['drop'])) {
|
||||
$suggestion = [
|
||||
'key' => 'deleteField',
|
||||
'label' => 'Drop fields (really!)',
|
||||
'enabled' => false,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($dropRename['drop'] as $hash => $statement) {
|
||||
$suggestion['children'][] = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
if (!empty($dropRename['drop_table'])) {
|
||||
$suggestion = [
|
||||
'key' => 'deleteTable',
|
||||
'label' => 'Drop tables (really!)',
|
||||
'enabled' => false,
|
||||
'children' => [],
|
||||
];
|
||||
foreach ($dropRename['drop_table'] as $hash => $statement) {
|
||||
$child = [
|
||||
'hash' => $hash,
|
||||
'statement' => $statement,
|
||||
];
|
||||
if (!empty($dropRename['tables_count'][$hash])) {
|
||||
$child['rowCount'] = $dropRename['tables_count'][$hash];
|
||||
}
|
||||
$suggestion['children'][] = $child;
|
||||
}
|
||||
$suggestions[] = $suggestion;
|
||||
}
|
||||
} catch (StatementException $e) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
$e->getMessage(),
|
||||
'Database analysis failed',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
'suggestions' => $suggestions,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply selected database changes
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function databaseAnalyzerExecuteAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$selectedHashes = $request->getParsedBody()['install']['hashes'] ?? [];
|
||||
if (empty($selectedHashes)) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'',
|
||||
'No database changes selected',
|
||||
FlashMessage::WARNING
|
||||
));
|
||||
} else {
|
||||
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
|
||||
$sqlStatements = $sqlReader->getCreateTableStatementArray($sqlReader->getTablesDefinitionString());
|
||||
$schemaMigrationService = GeneralUtility::makeInstance(SchemaMigrator::class);
|
||||
$statementHashesToPerform = array_flip($selectedHashes);
|
||||
$results = $schemaMigrationService->migrate($sqlStatements, $statementHashesToPerform);
|
||||
// Create error flash messages if any
|
||||
foreach ($results as $errorMessage) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Error: ' . $errorMessage,
|
||||
'Database update failed',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
}
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Executed database updates',
|
||||
'Executed database updates'
|
||||
));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear table overview statistics action
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function clearTablesStatsAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/ClearTables.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'clearTablesClearToken' => $formProtection->generateToken('installTool', 'clearTablesClear'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'stats' => (new ClearTableService())->getTableStatistics(),
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a specific table
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function clearTablesClearAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$table = $request->getParsedBody()['install']['table'];
|
||||
if (empty($table)) {
|
||||
throw new \RuntimeException(
|
||||
'No table name given',
|
||||
1501944076
|
||||
);
|
||||
}
|
||||
(new ClearTableService())->clearSelectedTable($table);
|
||||
$messageQueue = (new FlashMessageQueue('install'))->enqueue(
|
||||
new FlashMessage('Cleared table')
|
||||
);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* Create Admin Get Data action
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function createAdminGetDataAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/CreateAdmin.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'createAdminToken' => $formProtection->generateToken('installTool', 'createAdmin'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a backend administrator from given username and password
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function createAdminAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$username = preg_replace('/\\s/i', '', $request->getParsedBody()['install']['userName']);
|
||||
$password = $request->getParsedBody()['install']['userPassword'];
|
||||
$passwordCheck = $request->getParsedBody()['install']['userPasswordCheck'];
|
||||
$isSystemMaintainer = ((bool)$request->getParsedBody()['install']['userSystemMaintainer'] == '1') ? true : false;
|
||||
|
||||
$messages = new FlashMessageQueue('install');
|
||||
|
||||
if (strlen($username) < 1) {
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'No valid username given.',
|
||||
'Administrator user not created',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} elseif ($password !== $passwordCheck) {
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'Passwords do not match.',
|
||||
'Administrator user not created',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} elseif (strlen($password) < 8) {
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'Password must be at least eight characters long.',
|
||||
'Administrator user not created',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} else {
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$userExists = $connectionPool->getConnectionForTable('be_users')
|
||||
->count(
|
||||
'uid',
|
||||
'be_users',
|
||||
['username' => $username]
|
||||
);
|
||||
if ($userExists) {
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'A user with username "' . $username . '" exists already.',
|
||||
'Administrator user not created',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} else {
|
||||
$hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
|
||||
$hashedPassword = $hashInstance->getHashedPassword($password);
|
||||
$adminUserFields = [
|
||||
'username' => $username,
|
||||
'password' => $hashedPassword,
|
||||
'admin' => 1,
|
||||
'tstamp' => $GLOBALS['EXEC_TIME'],
|
||||
'crdate' => $GLOBALS['EXEC_TIME']
|
||||
];
|
||||
$connectionPool->getConnectionForTable('be_users')->insert('be_users', $adminUserFields);
|
||||
|
||||
if ($isSystemMaintainer) {
|
||||
|
||||
// Get the new admin user uid juste created
|
||||
$newAdminUserUid = (int)$connectionPool->getConnectionForTable('be_users')->lastInsertId('be_users');
|
||||
|
||||
// Get the list of the existing systemMaintainer
|
||||
$existingSystemMaintainersList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
|
||||
|
||||
// Add the new admin user to the existing systemMaintainer list
|
||||
$newSystemMaintainersList = $existingSystemMaintainersList;
|
||||
$newSystemMaintainersList[] = $newAdminUserUid;
|
||||
|
||||
// Update the LocalConfiguration.php file with the new list
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationManager->setLocalConfigurationValuesByPathValuePairs(
|
||||
['SYS/systemMaintainers' => $newSystemMaintainersList]
|
||||
);
|
||||
}
|
||||
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'',
|
||||
'Administrator created with username "' . $username . '".'
|
||||
));
|
||||
}
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messages,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry action of language packs module gets
|
||||
* * list of available languages with details like active or not and last update
|
||||
* * list of loaded extensions
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function languagePacksGetDataAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Maintenance/LanguagePacks.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'languagePacksActivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksActivateLanguage'),
|
||||
'languagePacksDeactivateLanguageToken' => $formProtection->generateToken('installTool', 'languagePacksDeactivateLanguage'),
|
||||
'languagePacksUpdatePackToken' => $formProtection->generateToken('installTool', 'languagePacksUpdatePack'),
|
||||
'languagePacksUpdateIsoTimesToken' => $formProtection->generateToken('installTool', 'languagePacksUpdateIsoTimes'),
|
||||
]);
|
||||
// This action needs TYPO3_CONF_VARS for full GeneralUtility::getUrl() config
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$languagePacksService = GeneralUtility::makeInstance(LanguagePackService::class);
|
||||
$languagePacksService->updateMirrorBaseUrl();
|
||||
$extensions = $languagePacksService->getExtensionLanguagePackDetails();
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'languages' => $languagePacksService->getLanguageDetails(),
|
||||
'extensions' => $extensions,
|
||||
'activeLanguages' => $languagePacksService->getActiveLanguages(),
|
||||
'activeExtensions' => array_column($extensions, 'key'),
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a language and any possible dependency it may have
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function languagePacksActivateLanguageAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
$availableLanguages = $languagePackService->getAvailableLanguages();
|
||||
$activeLanguages = $languagePackService->getActiveLanguages();
|
||||
$iso = $request->getParsedBody()['install']['iso'];
|
||||
$activateArray = [];
|
||||
foreach ($availableLanguages as $availableIso => $name) {
|
||||
if ($availableIso === $iso && !in_array($availableIso, $activeLanguages, true)) {
|
||||
$activateArray[] = $iso;
|
||||
$dependencies = $locales->getLocaleDependencies($availableIso);
|
||||
if (!empty($dependencies)) {
|
||||
foreach ($dependencies as $dependency) {
|
||||
if (!in_array($dependency, $activeLanguages, true)) {
|
||||
$activateArray[] = $dependency;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($activateArray)) {
|
||||
$activeLanguages = array_merge($activeLanguages, $activateArray);
|
||||
sort($activeLanguages);
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationManager->setLocalConfigurationValueByPath(
|
||||
'EXTCONF/lang',
|
||||
['availableLanguages' => $activeLanguages]
|
||||
);
|
||||
$activationArray = [];
|
||||
foreach ($activateArray as $activateIso) {
|
||||
$activationArray[] = $availableLanguages[$activateIso] . ' (' . $activateIso . ')';
|
||||
}
|
||||
$messageQueue->enqueue(
|
||||
new FlashMessage(
|
||||
'These languages have been activated: ' . implode(', ', $activationArray)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$messageQueue->enqueue(
|
||||
new FlashMessage('Language with ISO code "' . $iso . '" not found or already active.', '', FlashMessage::ERROR)
|
||||
);
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a language if no other active language depends on it
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function languagePacksDeactivateLanguageAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
$availableLanguages = $languagePackService->getAvailableLanguages();
|
||||
$activeLanguages = $languagePackService->getActiveLanguages();
|
||||
$iso = $request->getParsedBody()['install']['iso'];
|
||||
if (empty($iso)) {
|
||||
throw new \RuntimeException('No iso code given', 1520109807);
|
||||
}
|
||||
$otherActiveLanguageDependencies = [];
|
||||
foreach ($activeLanguages as $activeLanguage) {
|
||||
if ($activeLanguage === $iso) {
|
||||
continue;
|
||||
}
|
||||
$dependencies = $locales->getLocaleDependencies($activeLanguage);
|
||||
if (in_array($iso, $dependencies, true)) {
|
||||
$otherActiveLanguageDependencies[] = $activeLanguage;
|
||||
}
|
||||
}
|
||||
if (!empty($otherActiveLanguageDependencies)) {
|
||||
// Error: Must disable dependencies first
|
||||
$dependentArray = [];
|
||||
foreach ($otherActiveLanguageDependencies as $dependency) {
|
||||
$dependentArray[] = $availableLanguages[$dependency] . ' (' . $dependency . ')';
|
||||
}
|
||||
$messageQueue->enqueue(
|
||||
new FlashMessage(
|
||||
'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" can not be deactivated. These'
|
||||
. ' other languages depend on it and need to be deactivated before:'
|
||||
. implode(', ', $dependentArray),
|
||||
'',
|
||||
FlashMessage::ERROR
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if (in_array($iso, $activeLanguages, true)) {
|
||||
// Deactivate this language
|
||||
$newActiveLanguages = [];
|
||||
foreach ($activeLanguages as $activeLanguage) {
|
||||
if ($activeLanguage === $iso) {
|
||||
continue;
|
||||
}
|
||||
$newActiveLanguages[] = $activeLanguage;
|
||||
}
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationManager->setLocalConfigurationValueByPath(
|
||||
'EXTCONF/lang',
|
||||
['availableLanguages' => $newActiveLanguages]
|
||||
);
|
||||
$messageQueue->enqueue(
|
||||
new FlashMessage(
|
||||
'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has been deactivated'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$messageQueue->enqueue(
|
||||
new FlashMessage(
|
||||
'Language "' . $availableLanguages[$iso] . ' (' . $iso . ')" has not been deactivated',
|
||||
'',
|
||||
FlashMessage::ERROR
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a pack of one extension and one language
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function languagePacksUpdatePackAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$this->loadExtLocalconfDatabaseAndExtTables();
|
||||
$iso = $request->getParsedBody()['install']['iso'];
|
||||
$key = $request->getParsedBody()['install']['extension'];
|
||||
$languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'packResult' => $languagePackService->languagePackDownload($key, $iso)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set "last updated" time in registry for fully updated language packs.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function languagePacksUpdateIsoTimesAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$isos = $request->getParsedBody()['install']['isos'];
|
||||
$languagePackService = GeneralUtility::makeInstance(LanguagePackService::class);
|
||||
$languagePackService->setLastUpdatedIsoCode($isos);
|
||||
|
||||
// The cache manager is already instantiated in the install tool
|
||||
// with some hacked settings to disable caching of extbase and fluid.
|
||||
// We want a "fresh" object here to operate on a different cache setup.
|
||||
// cacheManager implements SingletonInterface, so the only way to get a "fresh"
|
||||
// instance is by circumventing makeInstance and/or the objectManager and
|
||||
// using new directly!
|
||||
$cacheManager = new \TYPO3\CMS\Core\Cache\CacheManager();
|
||||
$cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
|
||||
$cacheManager->getCache('l10n')->flush();
|
||||
|
||||
return new JsonResponse(['success' => true]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set 'uc' field of all backend users to empty string
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function resetBackendUserUcAction(): ResponseInterface
|
||||
{
|
||||
GeneralUtility::makeInstance(ConnectionPool::class)
|
||||
->getQueryBuilderForTable('be_users')
|
||||
->update('be_users')
|
||||
->set('uc', '')
|
||||
->execute();
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Preferences of all backend users have been reset',
|
||||
'Reset preferences of all backend users'
|
||||
));
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
<?php
|
||||
declare(strict_types = 1);
|
||||
namespace TYPO3\CMS\Install\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\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Configuration\ConfigurationManager;
|
||||
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
|
||||
use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
|
||||
use TYPO3\CMS\Core\Core\Bootstrap;
|
||||
use TYPO3\CMS\Core\Crypto\PasswordHashing\PasswordHashFactory;
|
||||
use TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
|
||||
use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
|
||||
use TYPO3\CMS\Core\Http\JsonResponse;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Install\Configuration\FeatureManager;
|
||||
use TYPO3\CMS\Install\Service\ExtensionConfigurationService;
|
||||
use TYPO3\CMS\Install\Service\LocalConfigurationValueService;
|
||||
|
||||
/**
|
||||
* Settings controller
|
||||
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class SettingsController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* Main "show the cards" view
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function cardsAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/Cards.html');
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change install tool password
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function changeInstallToolPasswordGetDataAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/ChangeInstallToolPassword.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'changeInstallToolPasswordToken' => $formProtection->generateToken('installTool', 'changeInstallToolPassword'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change install tool password
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function changeInstallToolPasswordAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$password = $request->getParsedBody()['install']['password'] ?? '';
|
||||
$passwordCheck = $request->getParsedBody()['install']['passwordCheck'];
|
||||
$messageQueue = new FlashMessageQueue('install');
|
||||
|
||||
if ($password !== $passwordCheck) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Install tool password not changed. Given passwords do not match.',
|
||||
'',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} elseif (strlen($password) < 8) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'Install tool password not changed. Given password must be at least eight characters long.',
|
||||
'',
|
||||
FlashMessage::ERROR
|
||||
));
|
||||
} else {
|
||||
$hashInstance = GeneralUtility::makeInstance(PasswordHashFactory::class)->getDefaultHashInstance('BE');
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationManager->setLocalConfigurationValueByPath(
|
||||
'BE/installToolPassword',
|
||||
$hashInstance->getHashedPassword($password)
|
||||
);
|
||||
$messageQueue->enqueue(new FlashMessage('Install tool password changed'));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of possible and active system maintainers
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function systemMaintainerGetListAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/SystemMaintainer.html');
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'systemMaintainerWriteToken' => $formProtection->generateToken('installTool', 'systemMaintainerWrite'),
|
||||
'systemMaintainerIsDevelopmentContext' => GeneralUtility::getApplicationContext()->isDevelopment(),
|
||||
]);
|
||||
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
|
||||
// We have to respect the enable fields here by our own because no TCA is loaded
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable('be_users');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
$users = $queryBuilder
|
||||
->select('uid', 'username', 'disable', 'starttime', 'endtime')
|
||||
->from('be_users')
|
||||
->where(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('admin', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->neq('username', $queryBuilder->createNamedParameter('_cli_', \PDO::PARAM_STR))
|
||||
)
|
||||
)
|
||||
->orderBy('uid')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
$systemMaintainerList = $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? [];
|
||||
$systemMaintainerList = array_map('intval', $systemMaintainerList);
|
||||
$currentTime = time();
|
||||
foreach ($users as &$user) {
|
||||
$user['disable'] = $user['disable'] ||
|
||||
((int)$user['starttime'] !== 0 && $user['starttime'] > $currentTime) ||
|
||||
((int)$user['endtime'] !== 0 && $user['endtime'] < $currentTime);
|
||||
$user['isSystemMaintainer'] = in_array((int)$user['uid'], $systemMaintainerList, true);
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => [],
|
||||
'users' => $users,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write new system maintainer list
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function systemMaintainerWriteAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// Sanitize given user list and write out
|
||||
$newUserList = [];
|
||||
$users = $request->getParsedBody()['install']['users'] ?? [];
|
||||
if (is_array($users)) {
|
||||
foreach ($users as $uid) {
|
||||
if (MathUtility::canBeInterpretedAsInteger($uid)) {
|
||||
$newUserList[] = (int)$uid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
|
||||
$validatedUserList = $queryBuilder
|
||||
->select('uid')
|
||||
->from('be_users')
|
||||
->where(
|
||||
$queryBuilder->expr()->andX(
|
||||
$queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('admin', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($newUserList, Connection::PARAM_INT_ARRAY))
|
||||
)
|
||||
)->execute()->fetchAll();
|
||||
|
||||
$validatedUserList = array_column($validatedUserList, 'uid');
|
||||
$validatedUserList = array_map('intval', $validatedUserList);
|
||||
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationManager->setLocalConfigurationValuesByPathValuePairs(
|
||||
['SYS/systemMaintainers' => $validatedUserList]
|
||||
);
|
||||
|
||||
$messages = [];
|
||||
if (empty($validatedUserList)) {
|
||||
$messages[] = new FlashMessage(
|
||||
'',
|
||||
'Set system maintainer list to an empty array',
|
||||
FlashMessage::INFO
|
||||
);
|
||||
} else {
|
||||
$messages[] = new FlashMessage(
|
||||
implode(', ', $validatedUserList),
|
||||
'New system maintainer uid list',
|
||||
FlashMessage::INFO
|
||||
);
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messages
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main LocalConfiguration card content
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function localConfigurationGetContentAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$localConfigurationValueService = new LocalConfigurationValueService();
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/LocalConfigurationGetContent.html');
|
||||
$view->assignMultiple([
|
||||
'localConfigurationWriteToken' => $formProtection->generateToken('installTool', 'localConfigurationWrite'),
|
||||
'localConfigurationData' => $localConfigurationValueService->getCurrentConfigurationData(),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write given LocalConfiguration settings
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function localConfigurationWriteAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$settings = $request->getParsedBody()['install']['configurationValues'];
|
||||
if (!is_array($settings) || empty($settings)) {
|
||||
throw new \RuntimeException(
|
||||
'Expected value array not found',
|
||||
1502282283
|
||||
);
|
||||
}
|
||||
$localConfigurationValueService = new LocalConfigurationValueService();
|
||||
$messageQueue = $localConfigurationValueService->updateLocalConfigurationValues($settings);
|
||||
if (empty($messageQueue)) {
|
||||
$messageQueue->enqueue(new FlashMessage(
|
||||
'',
|
||||
'No values changed',
|
||||
FlashMessage::WARNING
|
||||
));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messageQueue,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main preset card content
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function presetsGetContentAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/PresetsGetContent.html');
|
||||
$presetFeatures = GeneralUtility::makeInstance(FeatureManager::class);
|
||||
$presetFeatures = $presetFeatures->getInitializedFeatures($request->getParsedBody()['install']['values'] ?? []);
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view->assignMultiple([
|
||||
'presetsActivateToken' => $formProtection->generateToken('installTool', 'presetsActivate'),
|
||||
// This action is called again from within the card itself if a custom image path is supplied
|
||||
'presetsGetContentToken' => $formProtection->generateToken('installTool', 'presetsGetContent'),
|
||||
'presetFeatures' => $presetFeatures,
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write selected presets
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function presetsActivateAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$messages = new FlashMessageQueue('install');
|
||||
$configurationManager = new ConfigurationManager();
|
||||
$featureManager = new FeatureManager();
|
||||
$configurationValues = $featureManager->getConfigurationForSelectedFeaturePresets($request->getParsedBody()['install']['values'] ?? []);
|
||||
if (!empty($configurationValues)) {
|
||||
$configurationManager->setLocalConfigurationValuesByPathValuePairs($configurationValues);
|
||||
$messageBody = [];
|
||||
foreach ($configurationValues as $configurationKey => $configurationValue) {
|
||||
$messageBody[] = '\'' . $configurationKey . '\' => \'' . $configurationValue . '\'';
|
||||
}
|
||||
$messages->enqueue(new FlashMessage(
|
||||
implode(', ', $messageBody),
|
||||
'Configuration written'
|
||||
));
|
||||
} else {
|
||||
$messages->enqueue(new FlashMessage(
|
||||
'',
|
||||
'No configuration change selected',
|
||||
FlashMessage::INFO
|
||||
));
|
||||
}
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messages,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of extensions with their configuration form.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function extensionConfigurationGetContentAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
// Extension configuration needs initialized $GLOBALS['LANG']
|
||||
Bootstrap::initializeLanguageObject();
|
||||
$extensionConfigurationService = new ExtensionConfigurationService();
|
||||
$extensionsWithConfigurations = [];
|
||||
$activePackages = GeneralUtility::makeInstance(PackageManager::class)->getActivePackages();
|
||||
foreach ($activePackages as $extensionKey => $activePackage) {
|
||||
if (@file_exists($activePackage->getPackagePath() . 'ext_conf_template.txt')) {
|
||||
$extensionsWithConfigurations[$extensionKey] = [
|
||||
'packageInfo' => $activePackage,
|
||||
'configuration' => $extensionConfigurationService->getConfigurationPreparedForView($extensionKey),
|
||||
];
|
||||
}
|
||||
}
|
||||
ksort($extensionsWithConfigurations);
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/ExtensionConfigurationGetContent.html');
|
||||
$view->assignMultiple([
|
||||
'extensionsWithConfigurations' => $extensionsWithConfigurations,
|
||||
'extensionConfigurationWriteToken' => $formProtection->generateToken('installTool', 'extensionConfigurationWrite'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write extension configuration
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function extensionConfigurationWriteAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$extensionKey = $request->getParsedBody()['install']['extensionKey'];
|
||||
$configuration = $request->getParsedBody()['install']['extensionConfiguration'];
|
||||
$nestedConfiguration = [];
|
||||
foreach ($configuration as $configKey => $value) {
|
||||
$nestedConfiguration = ArrayUtility::setValueByPath($nestedConfiguration, $configKey, $value, '.');
|
||||
}
|
||||
(new ExtensionConfiguration())->set($extensionKey, '', $nestedConfiguration);
|
||||
$messages = [
|
||||
new FlashMessage(
|
||||
'Successfully saved configuration for extension "' . $extensionKey . '"',
|
||||
'',
|
||||
FlashMessage::OK
|
||||
)
|
||||
];
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messages,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render feature toggles
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function featuresGetContentAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$configurationDescription = GeneralUtility::makeInstance(YamlFileLoader::class)
|
||||
->load($configurationManager->getDefaultConfigurationDescriptionFileLocation());
|
||||
$allFeatures = $GLOBALS['TYPO3_CONF_VARS']['SYS']['features'] ?? [];
|
||||
$features = [];
|
||||
foreach ($allFeatures as $featureName => $featureValue) {
|
||||
// Only features that have a .yml description will be listed. There is currently no
|
||||
// way for extensions to extend this, so feature toggles of non-core extensions are
|
||||
// not listed here.
|
||||
if (isset($configurationDescription['SYS']['items']['features']['items'][$featureName]['description'])) {
|
||||
$default = $configurationManager->getDefaultConfigurationValueByPath('SYS/features/' . $featureName);
|
||||
$features[] = [
|
||||
'name' => $featureName,
|
||||
'description' => $configurationDescription['SYS']['items']['features']['items'][$featureName]['description'],
|
||||
'default' => $default,
|
||||
'value' => $featureValue,
|
||||
];
|
||||
}
|
||||
}
|
||||
$formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
|
||||
$view = $this->initializeStandaloneView($request, 'Settings/FeaturesGetContent.html');
|
||||
$view->assignMultiple([
|
||||
'features' => $features,
|
||||
'featuresSaveToken' => $formProtection->generateToken('installTool', 'featuresSave'),
|
||||
]);
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'html' => $view->render(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update feature toggles state
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function featuresSaveAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
|
||||
$enabledFeaturesFromPost = $request->getParsedBody()['install']['values'] ?? [];
|
||||
$allFeatures = array_keys($GLOBALS['TYPO3_CONF_VARS']['SYS']['features'] ?? []);
|
||||
$configurationDescription = GeneralUtility::makeInstance(YamlFileLoader::class)
|
||||
->load($configurationManager->getDefaultConfigurationDescriptionFileLocation());
|
||||
foreach ($allFeatures as $featureName) {
|
||||
// Only features that have a .yml description will be listed. There is currently no
|
||||
// way for extensions to extend this, so feature toggles of non-core extensions are
|
||||
// not considered.
|
||||
if (isset($configurationDescription['SYS']['items']['features']['items'][$featureName]['description'])) {
|
||||
if (isset($enabledFeaturesFromPost[$featureName])) {
|
||||
$configurationManager->enableFeature($featureName);
|
||||
} else {
|
||||
$configurationManager->disableFeature($featureName);
|
||||
}
|
||||
}
|
||||
}
|
||||
$messages = [
|
||||
new FlashMessage(
|
||||
'Successfully updated feature toggles',
|
||||
'',
|
||||
FlashMessage::OK
|
||||
)
|
||||
];
|
||||
return new JsonResponse([
|
||||
'success' => true,
|
||||
'status' => $messages,
|
||||
]);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user