| Current Path : /var/www/surf/TYPO3/vendor/typo3/cms-install/Classes/Controller/ |
| Current File : /var/www/surf/TYPO3/vendor/typo3/cms-install/Classes/Controller/EnvironmentController.php |
<?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\Install\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;
use TYPO3\CMS\Backend\Toolbar\Enumeration\InformationStatus;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Imaging\GraphicalFunctions;
use TYPO3\CMS\Core\Mail\FluidEmail;
use TYPO3\CMS\Core\Mail\MailerInterface;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Core\Utility\CommandUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Install\FolderStructure\DefaultFactory;
use TYPO3\CMS\Install\FolderStructure\DefaultPermissionsCheck;
use TYPO3\CMS\Install\Service\LateBootService;
use TYPO3\CMS\Install\SystemEnvironment\Check;
use TYPO3\CMS\Install\SystemEnvironment\DatabaseCheck;
use TYPO3\CMS\Install\SystemEnvironment\ServerResponse\ServerResponseCheck;
use TYPO3\CMS\Install\SystemEnvironment\SetupCheck;
use TYPO3\CMS\Install\WebserverType;
/**
* Environment controller
* @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
*/
class EnvironmentController extends AbstractController
{
private const IMAGE_FILE_EXT = ['gif', 'jpg', 'png', 'tif', 'ai', 'pdf', 'webp'];
private const TEST_REFERENCE_PATH = __DIR__ . '/../../Resources/Public/Images/TestReference';
public function __construct(
private readonly LateBootService $lateBootService,
private readonly FormProtectionFactory $formProtectionFactory,
private readonly MailerInterface $mailer,
) {}
/**
* Main "show the cards" view
*/
public function cardsAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
return new JsonResponse([
'success' => true,
'html' => $view->render('Environment/Cards'),
]);
}
/**
* System Information Get Data action
*/
public function systemInformationGetDataAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
$view->assignMultiple([
'systemInformationCgiDetected' => Environment::isRunningOnCgiServer(),
'systemInformationDatabaseConnections' => $this->getDatabaseConnectionInformation(),
'systemInformationOperatingSystem' => Environment::isWindows() ? 'Windows' : 'Unix',
'systemInformationApplicationContext' => $this->getApplicationContextInformation(),
'phpVersion' => PHP_VERSION,
]);
return new JsonResponse([
'success' => true,
'html' => $view->render('Environment/SystemInformation'),
]);
}
/**
* System Information Get Data action
*/
public function phpInfoGetDataAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
return new JsonResponse([
'success' => true,
'html' => $view->render('Environment/PhpInfo'),
]);
}
/**
* Get environment status
*/
public function environmentCheckGetStatusAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
$messageQueue = new FlashMessageQueue('install');
$checkMessages = (new Check())->getStatus();
foreach ($checkMessages as $message) {
$messageQueue->enqueue($message);
}
$setupMessages = (new SetupCheck())->getStatus();
foreach ($setupMessages as $message) {
$messageQueue->enqueue($message);
}
$databaseMessages = (new DatabaseCheck())->getStatus();
foreach ($databaseMessages as $message) {
$messageQueue->enqueue($message);
}
$serverResponseMessages = (new ServerResponseCheck(false))->getStatus();
foreach ($serverResponseMessages as $message) {
$messageQueue->enqueue($message);
}
return new JsonResponse([
'success' => true,
'status' => [
'error' => $messageQueue->getAllMessages(ContextualFeedbackSeverity::ERROR),
'warning' => $messageQueue->getAllMessages(ContextualFeedbackSeverity::WARNING),
'ok' => $messageQueue->getAllMessages(ContextualFeedbackSeverity::OK),
'information' => $messageQueue->getAllMessages(ContextualFeedbackSeverity::INFO),
'notice' => $messageQueue->getAllMessages(ContextualFeedbackSeverity::NOTICE),
],
'html' => $view->render('Environment/EnvironmentCheck'),
'buttons' => [
[
'btnClass' => 'btn-default t3js-environmentCheck-execute',
'text' => 'Run tests again',
],
],
]);
}
/**
* Get folder structure status
*/
public function folderStructureGetStatusAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
$folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class);
$structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request));
$structureMessages = $structureFacade->getStatus();
$errorQueue = new FlashMessageQueue('install');
$okQueue = new FlashMessageQueue('install');
foreach ($structureMessages as $message) {
if ($message->getSeverity() === ContextualFeedbackSeverity::ERROR
|| $message->getSeverity() === ContextualFeedbackSeverity::WARNING
) {
$errorQueue->enqueue($message);
} else {
$okQueue->enqueue($message);
}
}
$permissionCheck = GeneralUtility::makeInstance(DefaultPermissionsCheck::class);
$view->assign('publicPath', Environment::getPublicPath());
$buttons = [];
if ($errorQueue->count() > 0) {
$buttons[] = [
'btnClass' => 'btn-default t3js-folderStructure-errors-fix',
'text' => 'Try to fix file and folder permissions',
];
}
return new JsonResponse([
'success' => true,
'errorStatus' => $errorQueue,
'okStatus' => $okQueue,
'folderStructureFilePermissionStatus' => $permissionCheck->getMaskStatus('fileCreateMask'),
'folderStructureDirectoryPermissionStatus' => $permissionCheck->getMaskStatus('folderCreateMask'),
'html' => $view->render('Environment/FolderStructure'),
'buttons' => $buttons,
]);
}
/**
* Try to fix folder structure errors
*/
public function folderStructureFixAction(ServerRequestInterface $request): ResponseInterface
{
$folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class);
$structureFacade = $folderStructureFactory->getStructure(WebserverType::fromRequest($request));
$fixedStatusObjects = $structureFacade->fix();
return new JsonResponse([
'success' => true,
'fixedStatus' => $fixedStatusObjects,
]);
}
/**
* System Information Get Data action
*/
public function mailTestGetDataAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
$formProtection = $this->formProtectionFactory->createFromRequest($request);
$isSendPossible = true;
$messages = new FlashMessageQueue('install');
$senderEmail = $this->getSenderEmailAddress();
if ($senderEmail === '') {
$messages->enqueue(new FlashMessage(
'Sender email address is not configured. Please configure $GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'defaultMailFromAddress\'].',
'Can not send mail',
ContextualFeedbackSeverity::ERROR
));
$isSendPossible = false;
} elseif (!GeneralUtility::validEmail($senderEmail)) {
$messages->enqueue(new FlashMessage(
sprintf(
'Sender email address <%s> is configured, but is not a valid email. Please use a valid email address in $GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'defaultMailFromAddress\'].',
$senderEmail
),
'Can not send mail',
ContextualFeedbackSeverity::ERROR
));
$isSendPossible = false;
}
$view->assignMultiple([
'mailTestToken' => $formProtection->generateToken('installTool', 'mailTest'),
'mailTestSenderAddress' => $this->getSenderEmailAddress(),
'isSendPossible' => $isSendPossible,
'queueIdentifier' => 'install',
]);
return new JsonResponse([
'success' => true,
'messages' => $messages,
'sendPossible' => $isSendPossible,
'html' => $view->render('Environment/MailTest'),
'buttons' => [
[
'btnClass' => 'btn-default t3js-mailTest-execute',
'text' => 'Send test mail',
],
],
]);
}
/**
* Send a test mail
*/
public function mailTestAction(ServerRequestInterface $request): ResponseInterface
{
$container = $this->lateBootService->getContainer();
$backup = $this->lateBootService->makeCurrent($container);
$messages = new FlashMessageQueue('install');
$recipient = $request->getParsedBody()['install']['email'];
if (empty($recipient) || !GeneralUtility::validEmail($recipient)) {
$messages->enqueue(new FlashMessage(
'Given recipient address is not a valid email address.',
'Mail not sent',
ContextualFeedbackSeverity::ERROR
));
} else {
try {
$variables = [
'headline' => 'TYPO3 Test Mail',
'introduction' => 'Hey TYPO3 Administrator',
'content' => 'Seems like your favorite TYPO3 installation can send out emails!',
];
$mailMessage = GeneralUtility::makeInstance(FluidEmail::class);
$mailMessage
->to($recipient)
->from(new Address($this->getSenderEmailAddress(), $this->getSenderEmailName()))
->subject($this->getEmailSubject())
->setRequest($request)
->assignMultiple($variables);
$this->mailer->send($mailMessage);
$messages->enqueue(new FlashMessage(
'Recipient: ' . $recipient,
'Test mail sent'
));
} catch (RfcComplianceException $exception) {
$messages->enqueue(new FlashMessage(
'Please verify $GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][\'defaultMailFromAddress\'] is a valid mail address.'
. ' Error message: ' . $exception->getMessage(),
'RFC compliance problem',
ContextualFeedbackSeverity::ERROR
));
} catch (\Throwable $throwable) {
$messages->enqueue(new FlashMessage(
'Please verify $GLOBALS[\'TYPO3_CONF_VARS\'][\'MAIL\'][*] settings are valid.'
. ' Error message: ' . $throwable->getMessage(),
'Could not deliver mail',
ContextualFeedbackSeverity::ERROR
));
}
}
$this->lateBootService->makeCurrent(null, $backup);
return new JsonResponse([
'success' => true,
'status' => $messages,
]);
}
/**
* System Information Get Data action
*/
public function imageProcessingGetDataAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initializeView($request);
$view->assignMultiple([
'imageProcessingProcessor' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor'] === 'GraphicsMagick' ? 'GraphicsMagick' : 'ImageMagick',
'imageProcessingEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'],
'imageProcessingPath' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'],
'imageProcessingVersion' => $this->determineImageMagickVersion(),
'imageProcessingEffects' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_effects'],
'imageProcessingGdlibEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'],
'imageProcessingGdlibPng' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png'],
'imageProcessingFileFormats' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
]);
return new JsonResponse([
'success' => true,
'html' => $view->render('Environment/ImageProcessing'),
'buttons' => [
[
'btnClass' => 'btn-default disabled t3js-imageProcessing-execute',
'text' => 'Run image tests again',
],
],
]);
}
/**
* Create true type font test image
*/
public function imageProcessingTrueTypeAction(): ResponseInterface
{
$image = @imagecreate(200, 50);
imagecolorallocate($image, 255, 255, 55);
$textColor = imagecolorallocate($image, 233, 14, 91);
@imagettftext(
$image,
20 / 96.0 * 72, // As in compensateFontSizeBasedOnFreetypeDpi
0,
10,
20,
$textColor,
ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
'Testing true type'
);
$outputFile = Environment::getPublicPath() . '/typo3temp/assets/images/installTool-' . StringUtility::getUniqueId('createTrueTypeFontTestImage') . '.gif';
@imagegif($image, $outputFile);
$fileExists = file_exists($outputFile);
if ($fileExists) {
GeneralUtility::fixPermissions($outputFile);
}
$result = [
'fileExists' => $fileExists,
'referenceFile' => self::TEST_REFERENCE_PATH . '/Font.gif',
];
if ($fileExists) {
$result['outputFile'] = $outputFile;
}
return $this->getImageTestResponse($result);
}
/**
* Convert to jpg from jpg
*/
public function imageProcessingReadJpgAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('jpg');
}
/**
* Convert to jpg from gif
*/
public function imageProcessingReadGifAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('gif');
}
/**
* Convert to jpg from png
*/
public function imageProcessingReadPngAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('png');
}
/**
* Convert to jpg from tif
*/
public function imageProcessingReadTifAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('tif');
}
/**
* Convert to jpg from pdf
*/
public function imageProcessingReadPdfAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('pdf');
}
/**
* Convert to jpg from ai
*/
public function imageProcessingReadAiAction(): ResponseInterface
{
return $this->convertImageFormatsToJpg('ai');
}
/**
* Writing gif test
*/
public function imageProcessingWriteGifAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$inputFile = $imageBasePath . 'TestInput/Test.gif';
$imageProcessor = $this->initializeImageProcessor();
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('write-gif');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'gif', '300', '', '', '', [], true);
$messages = new FlashMessageQueue('install');
if ($imResult !== null && is_file($imResult[3])) {
if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gif_compress']) {
clearstatcache();
$previousSize = GeneralUtility::formatSize((int)filesize($imResult[3]));
$methodUsed = GraphicalFunctions::gifCompress($imResult[3], '');
clearstatcache();
$compressedSize = GeneralUtility::formatSize((int)filesize($imResult[3]));
$messages->enqueue(new FlashMessage(
'Method used by compress: ' . $methodUsed . LF
. ' Previous filesize: ' . $previousSize . '. Current filesize:' . $compressedSize,
'Compressed gif',
ContextualFeedbackSeverity::INFO
));
} else {
$messages->enqueue(new FlashMessage(
'',
'Gif compression not enabled by [GFX][gif_compress]',
ContextualFeedbackSeverity::INFO
));
}
$result = [
'status' => $messages,
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Write-gif.gif',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Writing png test
*/
public function imageProcessingWritePngAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$inputFile = $imageBasePath . 'TestInput/Test.png';
$imageProcessor = $this->initializeImageProcessor();
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('write-png');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'png', '300', '', '', '', [], true);
if ($imResult !== null && is_file($imResult[3])) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Write-png.png',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Writing webp test
*/
public function imageProcessingWriteWebpAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$inputFile = $imageBasePath . 'TestInput/Test.webp';
$imageProcessor = $this->initializeImageProcessor();
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('write-webp');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'webp', '300', '', '', '', [], true);
if ($imResult !== null && is_file($imResult[3])) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Write-webp.webp',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Scaling transparent files - gif to gif
*/
public function imageProcessingGifToGifAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/Transparent.gif';
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-gif');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'gif', '300', '', '', '', [], true);
if ($imResult !== null && file_exists($imResult[3])) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Scale-gif.gif',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Scaling transparent files - png to png
*/
public function imageProcessingPngToPngAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/Transparent.png';
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-png');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'png', '300', '', '', '', [], true);
if ($imResult !== null && file_exists($imResult[3])) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Scale-png.png',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Scaling transparent files - gif to jpg
*/
public function imageProcessingGifToJpgAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/Transparent.gif';
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-jpg');
$jpegQuality = MathUtility::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['GFX']['jpg_quality'], 10, 100, 85);
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'jpg', '300', '', '-quality ' . $jpegQuality . ' -opaque white -background white -flatten', '', [], true);
if ($imResult !== null && file_exists($imResult[3])) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Scale-jpg.jpg',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Converting jpg to webp
*/
public function imageProcessingJpgToWebpAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/Test.jpg';
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('read-webp');
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'webp', '300', '', '', '', [], true);
if ($imResult !== null) {
$result = [
'fileExists' => file_exists($imResult[3]),
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Convert-webp.webp',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Combine images with gif mask
*/
public function imageProcessingCombineGifMaskAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/BackgroundOrange.gif';
$overlayFile = $imageBasePath . 'TestInput/Test.jpg';
$maskFile = $imageBasePath . 'TestInput/MaskBlackWhite.gif';
$resultFile = $this->getImagesPath() . $imageProcessor->filenamePrefix
. StringUtility::getUniqueId($imageProcessor->alternativeOutputKey . 'combine1') . '.jpg';
$imageProcessor->combineExec($inputFile, $overlayFile, $maskFile, $resultFile);
$imResult = $imageProcessor->getImageDimensions($resultFile);
if ($imResult) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Combine-1.jpg',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Combine images with jpg mask
*/
public function imageProcessingCombineJpgMaskAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/BackgroundCombine.jpg';
$overlayFile = $imageBasePath . 'TestInput/Test.jpg';
$maskFile = $imageBasePath . 'TestInput/MaskCombine.jpg';
$resultFile = $this->getImagesPath() . $imageProcessor->filenamePrefix
. StringUtility::getUniqueId($imageProcessor->alternativeOutputKey . 'combine2') . '.jpg';
$imageProcessor->combineExec($inputFile, $overlayFile, $maskFile, $resultFile);
$imResult = $imageProcessor->getImageDimensions($resultFile);
if ($imResult) {
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Combine-2.jpg',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* GD with simple box
*/
public function imageProcessingGdlibSimpleAction(): ResponseInterface
{
$imageProcessor = $this->initializeImageProcessor();
$gifOrPng = $imageProcessor->gifExtension;
$image = imagecreatetruecolor(300, 225);
$backgroundColor = imagecolorallocate($image, 0, 0, 0);
imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
$workArea = [0, 0, 300, 225];
$conf = [
'dimensions' => '10,50,280,50',
'color' => 'olive',
];
$imageProcessor->makeBox($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdSimple') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$imResult = $imageProcessor->getImageDimensions($outputFile);
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Gdlib-simple.' . $gifOrPng,
'command' => $imageProcessor->IM_commands,
];
return $this->getImageTestResponse($result);
}
/**
* GD from image with box
*/
public function imageProcessingGdlibFromFileAction(): ResponseInterface
{
$imageProcessor = $this->initializeImageProcessor();
$gifOrPng = $imageProcessor->gifExtension;
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$inputFile = $imageBasePath . 'TestInput/Test.' . $gifOrPng;
$image = $imageProcessor->imageCreateFromFile($inputFile);
$workArea = [0, 0, 400, 300];
$conf = [
'dimensions' => '10,50,380,50',
'color' => 'olive',
];
$imageProcessor->makeBox($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdBox') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$imResult = $imageProcessor->getImageDimensions($outputFile);
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Gdlib-box.' . $gifOrPng,
'command' => $imageProcessor->IM_commands,
];
return $this->getImageTestResponse($result);
}
/**
* GD with text
*/
public function imageProcessingGdlibRenderTextAction(): ResponseInterface
{
$imageProcessor = $this->initializeImageProcessor();
$gifOrPng = $imageProcessor->gifExtension;
$image = imagecreatetruecolor(300, 225);
$backgroundColor = imagecolorallocate($image, 128, 128, 150);
imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
$workArea = [0, 0, 300, 225];
$conf = [
'iterations' => 1,
'angle' => 0,
'antiAlias' => 1,
'text' => 'HELLO WORLD',
'fontColor' => '#003366',
'fontSize' => 30,
'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
'offset' => '30,80',
];
$conf['BBOX'] = $imageProcessor->calcBBox($conf);
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$imResult = $imageProcessor->getImageDimensions($outputFile);
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Gdlib-text.' . $gifOrPng,
'command' => $imageProcessor->IM_commands,
];
return $this->getImageTestResponse($result);
}
/**
* GD with text, niceText
*/
public function imageProcessingGdlibNiceTextAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageProcessor = $this->initializeImageProcessor();
$gifOrPng = $imageProcessor->gifExtension;
$image = imagecreatetruecolor(300, 225);
$backgroundColor = imagecolorallocate($image, 128, 128, 150);
imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
$workArea = [0, 0, 300, 225];
$conf = [
'iterations' => 1,
'angle' => 0,
'antiAlias' => 1,
'text' => 'HELLO WORLD',
'fontColor' => '#003366',
'fontSize' => 30,
'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
'offset' => '30,80',
];
$conf['BBOX'] = $imageProcessor->calcBBox($conf);
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$conf['offset'] = '30,120';
$conf['niceText'] = 1;
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdNiceText') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$imResult = $imageProcessor->getImageDimensions($outputFile);
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Gdlib-niceText.' . $gifOrPng,
'command' => $imageProcessor->IM_commands,
];
return $this->getImageTestResponse($result);
}
/**
* GD with text, niceText, shadow
*/
public function imageProcessingGdlibNiceTextShadowAction(): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
$imageProcessor = $this->initializeImageProcessor();
$gifOrPng = $imageProcessor->gifExtension;
$image = imagecreatetruecolor(300, 225);
$backgroundColor = imagecolorallocate($image, 128, 128, 150);
imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
$workArea = [0, 0, 300, 225];
$conf = [
'iterations' => 1,
'angle' => 0,
'antiAlias' => 1,
'text' => 'HELLO WORLD',
'fontColor' => '#003366',
'fontSize' => 30,
'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
'offset' => '30,80',
];
$conf['BBOX'] = $imageProcessor->calcBBox($conf);
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$conf['offset'] = '30,120';
$conf['niceText'] = 1;
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdNiceText') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$conf['offset'] = '30,160';
$conf['niceText'] = 1;
$conf['shadow.'] = [
'offset' => '2,2',
'blur' => '20',
'opacity' => '50',
'color' => 'black',
];
// Warning: Re-uses $image from above!
$imageProcessor->makeShadow($image, $conf['shadow.'], $workArea, $conf);
$imageProcessor->makeText($image, $conf, $workArea);
$outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('GDwithText-niceText-shadow') . '.' . $gifOrPng;
$imageProcessor->ImageWrite($image, $outputFile);
$imResult = $imageProcessor->getImageDimensions($outputFile);
$result = [
'fileExists' => true,
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Gdlib-shadow.' . $gifOrPng,
'command' => $imageProcessor->IM_commands,
];
return $this->getImageTestResponse($result);
}
/**
* Initialize image processor
*
* @return GraphicalFunctions Initialized image processor
*/
protected function initializeImageProcessor(): GraphicalFunctions
{
$imageProcessor = GeneralUtility::makeInstance(GraphicalFunctions::class);
$imageProcessor->dontCheckForExistingTempFile = true;
$imageProcessor->filenamePrefix = 'installTool-';
$imageProcessor->dontCompress = true;
$imageProcessor->alternativeOutputKey = 'typo3InstallTest';
$imageProcessor->setImageFileExt(self::IMAGE_FILE_EXT);
return $imageProcessor;
}
/**
* Determine ImageMagick / GraphicsMagick version
*
* @return string Version
*/
protected function determineImageMagickVersion(): string
{
$command = CommandUtility::imageMagickCommand('identify', '-version');
CommandUtility::exec($command, $result);
$string = $result[0] ?? '';
$version = '';
if (!empty($string)) {
[, $version] = explode('Magick', $string);
[$version] = explode(' ', trim($version));
$version = trim($version);
}
return $version;
}
/**
* Convert to jpg from given input format
*/
protected function convertImageFormatsToJpg(string $inputFormat): ResponseInterface
{
if (!$this->isImageMagickEnabledAndConfigured()) {
return new JsonResponse([
'success' => true,
'status' => [$this->imageMagickDisabledMessage()],
]);
}
if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $inputFormat)) {
return new JsonResponse([
'success' => true,
'status' => [
new FlashMessage(
'Handling format ' . $inputFormat . ' must be enabled in TYPO3_CONF_VARS[\'GFX\'][\'imagefile_ext\']',
'Skipped test',
ContextualFeedbackSeverity::WARNING
),
],
]);
}
$imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
$imageProcessor = $this->initializeImageProcessor();
$inputFile = $imageBasePath . 'TestInput/Test.' . $inputFormat;
$imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('read') . '-' . $inputFormat;
$imResult = $imageProcessor->imageMagickConvert($inputFile, 'jpg', '300', '', '', '', [], true);
if ($imResult !== null) {
$result = [
'fileExists' => file_exists($imResult[3]),
'outputFile' => $imResult[3],
'referenceFile' => self::TEST_REFERENCE_PATH . '/Read-' . $inputFormat . '.jpg',
'command' => $imageProcessor->IM_commands,
];
} else {
$result = [
'status' => [$this->imageGenerationFailedMessage()],
'command' => $imageProcessor->IM_commands,
];
}
return $this->getImageTestResponse($result);
}
/**
* Get details about all configured database connections
*/
protected function getDatabaseConnectionInformation(): array
{
$connectionInfos = [];
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
foreach ($connectionPool->getConnectionNames() as $connectionName) {
$connection = $connectionPool->getConnectionByName($connectionName);
$connectionParameters = $connection->getParams();
$connectionInfo = [
'connectionName' => $connectionName,
'version' => $connection->getServerVersion(),
'databaseName' => $connection->getDatabase(),
'username' => $connectionParameters['user'] ?? '',
'host' => $connectionParameters['host'] ?? '',
'port' => $connectionParameters['port'] ?? '',
'socket' => $connectionParameters['unix_socket'] ?? '',
'numberOfTables' => count($connection->createSchemaManager()->listTableNames()),
'numberOfMappedTables' => 0,
];
if (isset($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
&& is_array($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
) {
// Count number of array keys having $connectionName as value
$connectionInfo['numberOfMappedTables'] = count(array_intersect(
$GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'],
[$connectionName]
));
}
$connectionInfos[] = $connectionInfo;
}
return $connectionInfos;
}
/**
* Get details about the application context
*/
protected function getApplicationContextInformation(): array
{
$applicationContext = Environment::getContext();
$status = $applicationContext->isProduction() ? InformationStatus::STATUS_OK : InformationStatus::STATUS_WARNING;
return [
'context' => (string)$applicationContext,
'status' => $status,
];
}
/**
* Get sender address from configuration
* ['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']
* If this setting is empty, return an empty string.
*
* Email servers often reject mails with an invalid sender email address (or an address which does not correspondent
* to the email account). In any case, it is not good practice to send emails with arbitrary sender addresses.
* This is why a default like 'no-reply@example.com' is no longer being used here. The sender address should
* be configured explicitly via ['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'].
*
* @return string Returns an email address
*/
protected function getSenderEmailAddress(): string
{
return $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'] ?? '';
}
/**
* Gets sender name from configuration
* ['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']
* If this setting is empty, it falls back to a default string.
*/
protected function getSenderEmailName(): string
{
return !empty($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'])
? $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']
: 'TYPO3 CMS install tool';
}
/**
* Gets email subject from configuration
* ['TYPO3_CONF_VARS']['SYS']['sitename']
* If this setting is empty, it falls back to a default string.
*/
protected function getEmailSubject(): string
{
$name = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'])
? ' from site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '"'
: '';
return 'Test TYPO3 CMS mail delivery' . $name;
}
/**
* Create a JsonResponse from single image tests
*/
protected function getImageTestResponse(array $testResult): ResponseInterface
{
$responseData = [
'success' => true,
];
foreach ($testResult as $resultKey => $value) {
if ($resultKey === 'referenceFile' && !empty($testResult['referenceFile'])) {
$referenceFileArray = explode('.', $testResult['referenceFile']);
$fileExt = end($referenceFileArray);
$responseData['referenceFile'] = 'data:image/' . $fileExt . ';base64,' . base64_encode((string)file_get_contents($testResult['referenceFile']));
} elseif ($resultKey === 'outputFile' && !empty($testResult['outputFile'])) {
$outputFileArray = explode('.', $testResult['outputFile']);
$fileExt = end($outputFileArray);
$responseData['outputFile'] = 'data:image/' . $fileExt . ';base64,' . base64_encode((string)file_get_contents($testResult['outputFile']));
} else {
$responseData[$resultKey] = $value;
}
}
return new JsonResponse($responseData);
}
/**
* Create a 'image generation failed' message
*/
protected function imageGenerationFailedMessage(): FlashMessage
{
return new FlashMessage(
'ImageMagick / GraphicsMagick handling is enabled, but the execute'
. ' command returned an error. Please check your settings, especially'
. ' [\'GFX\'][\'processor_path\'] and ensure Ghostscript is installed on your server.',
'Image generation failed',
ContextualFeedbackSeverity::ERROR
);
}
/**
* Find out if ImageMagick or GraphicsMagick is enabled and set up
*
* @return bool TRUE if enabled and path is set
*/
protected function isImageMagickEnabledAndConfigured(): bool
{
$enabled = $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'];
$path = $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'];
return $enabled && $path;
}
/**
* Create a 'imageMagick disabled' message
*/
protected function imageMagickDisabledMessage(): FlashMessage
{
return new FlashMessage(
'ImageMagick / GraphicsMagick handling is disabled or not configured correctly.',
'Tests not executed',
ContextualFeedbackSeverity::ERROR
);
}
/**
* Return the temp image dir.
* If not exist it will be created
*/
protected function getImagesPath(): string
{
$imagePath = Environment::getPublicPath() . '/typo3temp/assets/images/';
if (!is_dir($imagePath)) {
GeneralUtility::mkdir_deep($imagePath);
}
return $imagePath;
}
}