[TASK] Use Environment API instead of PATH_site in install
[Packages/TYPO3.CMS.git] / typo3 / sysext / install / Classes / Controller / EnvironmentController.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Install\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Core\Core\Environment;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
23 use TYPO3\CMS\Core\FormProtection\InstallToolFormProtection;
24 use TYPO3\CMS\Core\Http\JsonResponse;
25 use TYPO3\CMS\Core\Imaging\GraphicalFunctions;
26 use TYPO3\CMS\Core\Mail\MailMessage;
27 use TYPO3\CMS\Core\Messaging\FlashMessage;
28 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
29 use TYPO3\CMS\Core\Utility\CommandUtility;
30 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
31 use TYPO3\CMS\Core\Utility\GeneralUtility;
32 use TYPO3\CMS\Core\Utility\StringUtility;
33 use TYPO3\CMS\Install\FolderStructure\DefaultFactory;
34 use TYPO3\CMS\Install\FolderStructure\DefaultPermissionsCheck;
35 use TYPO3\CMS\Install\SystemEnvironment\Check;
36 use TYPO3\CMS\Install\SystemEnvironment\DatabaseCheck;
37 use TYPO3\CMS\Install\SystemEnvironment\SetupCheck;
38
39 /**
40 * Environment controller
41 */
42 class EnvironmentController extends AbstractController
43 {
44 /**
45 * Main "show the cards" view
46 *
47 * @param ServerRequestInterface $request
48 * @return ResponseInterface
49 */
50 public function cardsAction(ServerRequestInterface $request): ResponseInterface
51 {
52 $view = $this->initializeStandaloneView($request, 'Environment/Cards.html');
53 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
54 $view->assignMultiple([
55 'imageProcessingProcessor' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor'] === 'GraphicsMagick' ? 'GraphicsMagick' : 'ImageMagick',
56 'imageProcessingEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'],
57 'imageProcessingPath' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'],
58 'imageProcessingVersion' => $this->determineImageMagickVersion(),
59 'imageProcessingEffects' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_effects'],
60 'imageProcessingGdlibEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'],
61 'imageProcessingGdlibPng' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png'],
62 'imageProcessingFileFormats' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
63
64 'mailTestToken' => $formProtection->generateToken('installTool', 'mailTest'),
65 'mailTestSenderAddress' => $this->getSenderEmailAddress(),
66
67 'systemInformationCgiDetected', GeneralUtility::isRunningOnCgiServerApi(),
68 'systemInformationDatabaseConnections' => $this->getDatabaseConnectionInformation(),
69 'systemInformationOperatingSystem' => Environment::isWindows() ? 'Windows' : 'Unix',
70 ]);
71 return new JsonResponse([
72 'success' => true,
73 'html' => $view->render(),
74 ]);
75 }
76
77 /**
78 * System Information Get Data action
79 *
80 * @param $request ServerRequestInterface
81 * @return ResponseInterface
82 */
83 public function systemInformationGetDataAction(ServerRequestInterface $request): ResponseInterface
84 {
85 $view = $this->initializeStandaloneView($request, 'Environment/SystemInformation.html');
86 $view->assignMultiple([
87 'systemInformationCgiDetected', GeneralUtility::isRunningOnCgiServerApi(),
88 'systemInformationDatabaseConnections' => $this->getDatabaseConnectionInformation(),
89 'systemInformationOperatingSystem' => Environment::isWindows() ? 'Windows' : 'Unix',
90 ]);
91 return new JsonResponse([
92 'success' => true,
93 'html' => $view->render(),
94 ]);
95 }
96
97 /**
98 * System Information Get Data action
99 *
100 * @param $request ServerRequestInterface
101 * @return ResponseInterface
102 */
103 public function phpInfoGetDataAction(ServerRequestInterface $request): ResponseInterface
104 {
105 $view = $this->initializeStandaloneView($request, 'Environment/PhpInfo.html');
106 return new JsonResponse([
107 'success' => true,
108 'html' => $view->render(),
109 ]);
110 }
111
112 /**
113 * Get environment status
114 *
115 * @param $request ServerRequestInterface
116 * @return ResponseInterface
117 */
118 public function environmentCheckGetStatusAction(ServerRequestInterface $request): ResponseInterface
119 {
120 $view = $this->initializeStandaloneView($request, 'Environment/EnvironmentCheck.html');
121 $messageQueue = new FlashMessageQueue('install');
122 $checkMessages = (new Check())->getStatus();
123 foreach ($checkMessages as $message) {
124 $messageQueue->enqueue($message);
125 }
126 $setupMessages = (new SetupCheck())->getStatus();
127 foreach ($setupMessages as $message) {
128 $messageQueue->enqueue($message);
129 }
130 $databaseMessages = (new DatabaseCheck())->getStatus();
131 foreach ($databaseMessages as $message) {
132 $messageQueue->enqueue($message);
133 }
134 return new JsonResponse([
135 'success' => true,
136 'status' => [
137 'error' => $messageQueue->getAllMessages(FlashMessage::ERROR),
138 'warning' => $messageQueue->getAllMessages(FlashMessage::WARNING),
139 'ok' => $messageQueue->getAllMessages(FlashMessage::OK),
140 'information' => $messageQueue->getAllMessages(FlashMessage::INFO),
141 'notice' => $messageQueue->getAllMessages(FlashMessage::NOTICE),
142 ],
143 'html' => $view->render(),
144 ]);
145 }
146
147 /**
148 * Get folder structure status
149 *
150 * @param $request ServerRequestInterface
151 * @return ResponseInterface
152 */
153 public function folderStructureGetStatusAction(ServerRequestInterface $request): ResponseInterface
154 {
155 $view = $this->initializeStandaloneView($request, 'Environment/FolderStructure.html');
156 $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class);
157 $structureFacade = $folderStructureFactory->getStructure();
158
159 $structureMessages = $structureFacade->getStatus();
160 $errorQueue = new FlashMessageQueue('install');
161 $okQueue = new FlashMessageQueue('install');
162 foreach ($structureMessages as $message) {
163 if ($message->getSeverity() === FlashMessage::ERROR
164 || $message->getSeverity() === FlashMessage::WARNING
165 ) {
166 $errorQueue->enqueue($message);
167 } else {
168 $okQueue->enqueue($message);
169 }
170 }
171
172 $permissionCheck = GeneralUtility::makeInstance(DefaultPermissionsCheck::class);
173
174 $view->assign('publicPath', Environment::getPublicPath());
175
176 return new JsonResponse([
177 'success' => true,
178 'errorStatus' => $errorQueue,
179 'okStatus' => $okQueue,
180 'folderStructureFilePermissionStatus' => $permissionCheck->getMaskStatus('fileCreateMask'),
181 'folderStructureDirectoryPermissionStatus' => $permissionCheck->getMaskStatus('folderCreateMask'),
182 'html' => $view->render(),
183 ]);
184 }
185
186 /**
187 * Try to fix folder structure errors
188 *
189 * @return ResponseInterface
190 */
191 public function folderStructureFixAction(): ResponseInterface
192 {
193 $folderStructureFactory = GeneralUtility::makeInstance(DefaultFactory::class);
194 $structureFacade = $folderStructureFactory->getStructure();
195 $fixedStatusObjects = $structureFacade->fix();
196 return new JsonResponse([
197 'success' => true,
198 'fixedStatus' => $fixedStatusObjects,
199 ]);
200 }
201
202 /**
203 * System Information Get Data action
204 *
205 * @param $request ServerRequestInterface
206 * @return ResponseInterface
207 */
208 public function mailTestGetDataAction(ServerRequestInterface $request): ResponseInterface
209 {
210 $view = $this->initializeStandaloneView($request, 'Environment/MailTest.html');
211 $formProtection = FormProtectionFactory::get(InstallToolFormProtection::class);
212 $view->assignMultiple([
213 'mailTestToken' => $formProtection->generateToken('installTool', 'mailTest'),
214 'mailTestSenderAddress' => $this->getSenderEmailAddress(),
215 ]);
216 return new JsonResponse([
217 'success' => true,
218 'html' => $view->render(),
219 ]);
220 }
221
222 /**
223 * Send a test mail
224 *
225 * @return ResponseInterface
226 */
227 public function mailTestAction(ServerRequestInterface $request): ResponseInterface
228 {
229 $messages = new FlashMessageQueue('install');
230 $recipient = $request->getParsedBody()['install']['email'];
231 if (empty($recipient) || !GeneralUtility::validEmail($recipient)) {
232 $messages->enqueue(new FlashMessage(
233 'Given address is not a valid email address.',
234 'Mail not sent',
235 FlashMessage::ERROR
236 ));
237 } else {
238 $mailMessage = GeneralUtility::makeInstance(MailMessage::class);
239 $mailMessage
240 ->addTo($recipient)
241 ->addFrom($this->getSenderEmailAddress(), $this->getSenderEmailName())
242 ->setSubject($this->getEmailSubject())
243 ->setBody('<html><body>html test content</body></html>', 'text/html')
244 ->addPart('TEST CONTENT')
245 ->send();
246 $messages->enqueue(new FlashMessage(
247 'Recipient: ' . $recipient,
248 'Test mail sent'
249 ));
250 }
251 return new JsonResponse([
252 'success' => true,
253 'status' => $messages,
254 ]);
255 }
256
257 /**
258 * System Information Get Data action
259 *
260 * @param $request ServerRequestInterface
261 * @return ResponseInterface
262 */
263 public function imageProcessingGetDataAction(ServerRequestInterface $request): ResponseInterface
264 {
265 $view = $this->initializeStandaloneView($request, 'Environment/ImageProcessing.html');
266 $view->assignMultiple([
267 'imageProcessingProcessor' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor'] === 'GraphicsMagick' ? 'GraphicsMagick' : 'ImageMagick',
268 'imageProcessingEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'],
269 'imageProcessingPath' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'],
270 'imageProcessingVersion' => $this->determineImageMagickVersion(),
271 'imageProcessingEffects' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_effects'],
272 'imageProcessingGdlibEnabled' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib'],
273 'imageProcessingGdlibPng' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png'],
274 'imageProcessingFileFormats' => $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'],
275 ]);
276 return new JsonResponse([
277 'success' => true,
278 'html' => $view->render(),
279 ]);
280 }
281
282 /**
283 * Create true type font test image
284 *
285 * @return ResponseInterface
286 */
287 public function imageProcessingTrueTypeAction(): ResponseInterface
288 {
289 $image = @imagecreate(200, 50);
290 imagecolorallocate($image, 255, 255, 55);
291 $textColor = imagecolorallocate($image, 233, 14, 91);
292 @imagettftext(
293 $image,
294 20 / 96.0 * 72, // As in compensateFontSizeiBasedOnFreetypeDpi
295 0,
296 10,
297 20,
298 $textColor,
299 ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
300 'Testing true type'
301 );
302 $outputFile = Environment::getPublicPath() . '/typo3temp/assets/images/installTool-' . StringUtility::getUniqueId('createTrueTypeFontTestImage') . '.gif';
303 imagegif($image, $outputFile);
304 $fileExists = file_exists($outputFile);
305 if ($fileExists) {
306 GeneralUtility::fixPermissions($outputFile);
307 }
308 return $this->getImageTestResponse([
309 'fileExists' => $fileExists,
310 'outputFile' => $outputFile,
311 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Font.gif',
312 ]);
313 }
314
315 /**
316 * Convert to jpg from jpg
317 *
318 * @return ResponseInterface
319 */
320 public function imageProcessingReadJpgAction(): ResponseInterface
321 {
322 return $this->convertImageFormatsToJpg('jpg');
323 }
324
325 /**
326 * Convert to jpg from gif
327 *
328 * @return ResponseInterface
329 */
330 public function imageProcessingReadGifAction(): ResponseInterface
331 {
332 return $this->convertImageFormatsToJpg('gif');
333 }
334
335 /**
336 * Convert to jpg from png
337 *
338 * @return ResponseInterface
339 */
340 public function imageProcessingReadPngAction(): ResponseInterface
341 {
342 return $this->convertImageFormatsToJpg('png');
343 }
344
345 /**
346 * Convert to jpg from tif
347 *
348 * @return ResponseInterface
349 */
350 public function imageProcessingReadTifAction(): ResponseInterface
351 {
352 return $this->convertImageFormatsToJpg('tif');
353 }
354
355 /**
356 * Convert to jpg from pdf
357 *
358 * @return ResponseInterface
359 */
360 public function imageProcessingReadPdfAction(): ResponseInterface
361 {
362 return $this->convertImageFormatsToJpg('pdf');
363 }
364
365 /**
366 * Convert to jpg from ai
367 *
368 * @return ResponseInterface
369 */
370 public function imageProcessingReadAiAction(): ResponseInterface
371 {
372 return $this->convertImageFormatsToJpg('ai');
373 }
374
375 /**
376 * Writing gif test
377 *
378 * @return ResponseInterface
379 */
380 public function imageProcessingWriteGifAction(): ResponseInterface
381 {
382 if (!$this->isImageMagickEnabledAndConfigured()) {
383 return new JsonResponse([
384 'status' => [$this->imageMagickDisabledMessage()],
385 ]);
386 }
387 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
388 $inputFile = $imageBasePath . 'TestInput/Test.gif';
389 $imageProcessor = $this->initializeImageProcessor();
390 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('write-gif');
391 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'gif', '300', '', '', '', [], true);
392 $messages = new FlashMessageQueue('install');
393 if ($imResult !== null && is_file($imResult[3])) {
394 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gif_compress']) {
395 clearstatcache();
396 $previousSize = GeneralUtility::formatSize(filesize($imResult[3]));
397 $methodUsed = GraphicalFunctions::gifCompress($imResult[3], '');
398 clearstatcache();
399 $compressedSize = GeneralUtility::formatSize(filesize($imResult[3]));
400 $messages->enqueue(new FlashMessage(
401 'Method used by compress: ' . $methodUsed . LF
402 . ' Previous filesize: ' . $previousSize . '. Current filesize:' . $compressedSize,
403 'Compressed gif',
404 FlashMessage::INFO
405 ));
406 } else {
407 $messages->enqueue(new FlashMessage(
408 '',
409 'Gif compression not enabled by [GFX][gif_compress]',
410 FlashMessage::INFO
411 ));
412 }
413 $result = [
414 'status' => $messages,
415 'fileExists' => true,
416 'outputFile' => $imResult[3],
417 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Write-gif.gif',
418 'command' => $imageProcessor->IM_commands,
419 ];
420 } else {
421 $result = [
422 'status' => [$this->imageGenerationFailedMessage()],
423 'command' => $imageProcessor->IM_commands,
424 ];
425 }
426 return $this->getImageTestResponse($result);
427 }
428
429 /**
430 * Writing png test
431 *
432 * @return ResponseInterface
433 */
434 public function imageProcessingWritePngAction(): ResponseInterface
435 {
436 if (!$this->isImageMagickEnabledAndConfigured()) {
437 return new JsonResponse([
438 'status' => [$this->imageMagickDisabledMessage()],
439 ]);
440 }
441 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
442 $inputFile = $imageBasePath . 'TestInput/Test.png';
443 $imageProcessor = $this->initializeImageProcessor();
444 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('write-png');
445 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'png', '300', '', '', '', [], true);
446 if ($imResult !== null && is_file($imResult[3])) {
447 $result = [
448 'fileExists' => true,
449 'outputFile' => $imResult[3],
450 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Write-png.png',
451 'command' => $imageProcessor->IM_commands,
452 ];
453 } else {
454 $result = [
455 'status' => [$this->imageGenerationFailedMessage()],
456 'command' => $imageProcessor->IM_commands,
457 ];
458 }
459 return $this->getImageTestResponse($result);
460 }
461
462 /**
463 * Scaling transparent files - gif to gif
464 *
465 * @return ResponseInterface
466 */
467 public function imageProcessingGifToGifAction(): ResponseInterface
468 {
469 if (!$this->isImageMagickEnabledAndConfigured()) {
470 return new JsonResponse([
471 'status' => [$this->imageMagickDisabledMessage()],
472 ]);
473 }
474 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
475 $imageProcessor = $this->initializeImageProcessor();
476 $inputFile = $imageBasePath . 'TestInput/Transparent.gif';
477 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-gif');
478 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'gif', '300', '', '', '', [], true);
479 if ($imResult !== null && file_exists($imResult[3])) {
480 $result = [
481 'fileExists' => true,
482 'outputFile' => $imResult[3],
483 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Scale-gif.gif',
484 'command' => $imageProcessor->IM_commands,
485 ];
486 } else {
487 $result = [
488 'status' => [$this->imageGenerationFailedMessage()],
489 'command' => $imageProcessor->IM_commands,
490 ];
491 }
492 return $this->getImageTestResponse($result);
493 }
494
495 /**
496 * Scaling transparent files - png to png
497 *
498 * @return ResponseInterface
499 */
500 public function imageProcessingPngToPngAction(): ResponseInterface
501 {
502 if (!$this->isImageMagickEnabledAndConfigured()) {
503 return new JsonResponse([
504 'status' => [$this->imageMagickDisabledMessage()],
505 ]);
506 }
507 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
508 $imageProcessor = $this->initializeImageProcessor();
509 $inputFile = $imageBasePath . 'TestInput/Transparent.png';
510 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-png');
511 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'png', '300', '', '', '', [], true);
512 if ($imResult !== null && file_exists($imResult[3])) {
513 $result = [
514 'fileExists' => true,
515 'outputFile' => $imResult[3],
516 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Scale-png.png',
517 'command' => $imageProcessor->IM_commands,
518 ];
519 } else {
520 $result = [
521 'status' => [$this->imageGenerationFailedMessage()],
522 'command' => $imageProcessor->IM_commands,
523 ];
524 }
525 return $this->getImageTestResponse($result);
526 }
527
528 /**
529 * Scaling transparent files - gif to jpg
530 *
531 * @return ResponseInterface
532 */
533 public function imageProcessingGifToJpgAction(): ResponseInterface
534 {
535 if (!$this->isImageMagickEnabledAndConfigured()) {
536 return new JsonResponse([
537 'status' => [$this->imageMagickDisabledMessage()],
538 ]);
539 }
540 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
541 $imageProcessor = $this->initializeImageProcessor();
542 $inputFile = $imageBasePath . 'TestInput/Transparent.gif';
543 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('scale-jpg');
544 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'jpg', '300', '', '-opaque white -background white -flatten', '', [], true);
545 if ($imResult !== null && file_exists($imResult[3])) {
546 $result = [
547 'fileExists' => true,
548 'outputFile' => $imResult[3],
549 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Scale-jpg.jpg',
550 'command' => $imageProcessor->IM_commands,
551 ];
552 } else {
553 $result = [
554 'status' => [$this->imageGenerationFailedMessage()],
555 'command' => $imageProcessor->IM_commands,
556 ];
557 }
558 return $this->getImageTestResponse($result);
559 }
560
561 /**
562 * Combine images with gif mask
563 *
564 * @return ResponseInterface
565 */
566 public function imageProcessingCombineGifMaskAction(): ResponseInterface
567 {
568 if (!$this->isImageMagickEnabledAndConfigured()) {
569 return new JsonResponse([
570 'status' => [$this->imageMagickDisabledMessage()],
571 ]);
572 }
573 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
574 $imageProcessor = $this->initializeImageProcessor();
575 $inputFile = $imageBasePath . 'TestInput/BackgroundOrange.gif';
576 $overlayFile = $imageBasePath . 'TestInput/Test.jpg';
577 $maskFile = $imageBasePath . 'TestInput/MaskBlackWhite.gif';
578 $resultFile = $this->getImagesPath() . $imageProcessor->filenamePrefix
579 . StringUtility::getUniqueId($imageProcessor->alternativeOutputKey . 'combine1') . '.jpg';
580 $imageProcessor->combineExec($inputFile, $overlayFile, $maskFile, $resultFile);
581 $imResult = $imageProcessor->getImageDimensions($resultFile);
582 if ($imResult) {
583 $result = [
584 'fileExists' => true,
585 'outputFile' => $imResult[3],
586 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Combine-1.jpg',
587 'command' => $imageProcessor->IM_commands,
588 ];
589 } else {
590 $result = [
591 'status' => [$this->imageGenerationFailedMessage()],
592 'command' => $imageProcessor->IM_commands,
593 ];
594 }
595 return $this->getImageTestResponse($result);
596 }
597
598 /**
599 * Combine images with jpg mask
600 *
601 * @return ResponseInterface
602 */
603 public function imageProcessingCombineJpgMaskAction(): ResponseInterface
604 {
605 if (!$this->isImageMagickEnabledAndConfigured()) {
606 return new JsonResponse([
607 'status' => [$this->imageMagickDisabledMessage()],
608 ]);
609 }
610 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
611 $imageProcessor = $this->initializeImageProcessor();
612 $inputFile = $imageBasePath . 'TestInput/BackgroundCombine.jpg';
613 $overlayFile = $imageBasePath . 'TestInput/Test.jpg';
614 $maskFile = $imageBasePath . 'TestInput/MaskCombine.jpg';
615 $resultFile = $this->getImagesPath() . $imageProcessor->filenamePrefix
616 . StringUtility::getUniqueId($imageProcessor->alternativeOutputKey . 'combine2') . '.jpg';
617 $imageProcessor->combineExec($inputFile, $overlayFile, $maskFile, $resultFile);
618 $imResult = $imageProcessor->getImageDimensions($resultFile);
619 if ($imResult) {
620 $result = [
621 'fileExists' => true,
622 'outputFile' => $imResult[3],
623 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Combine-2.jpg',
624 'command' => $imageProcessor->IM_commands,
625 ];
626 } else {
627 $result = [
628 'status' => [$this->imageGenerationFailedMessage()],
629 'command' => $imageProcessor->IM_commands,
630 ];
631 }
632 return $this->getImageTestResponse($result);
633 }
634
635 /**
636 * GD with simple box
637 *
638 * @return ResponseInterface
639 */
640 public function imageProcessingGdlibSimpleAction(): ResponseInterface
641 {
642 $imageProcessor = $this->initializeImageProcessor();
643 $gifOrPng = $imageProcessor->gifExtension;
644 $image = imagecreatetruecolor(300, 225);
645 $backgroundColor = imagecolorallocate($image, 0, 0, 0);
646 imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
647 $workArea = [0, 0, 300, 225];
648 $conf = [
649 'dimensions' => '10,50,280,50',
650 'color' => 'olive',
651 ];
652 $imageProcessor->makeBox($image, $conf, $workArea);
653 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdSimple') . '.' . $gifOrPng;
654 $imageProcessor->ImageWrite($image, $outputFile);
655 $imResult = $imageProcessor->getImageDimensions($outputFile);
656 $result = [
657 'fileExists' => true,
658 'outputFile' => $imResult[3],
659 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Gdlib-simple.' . $gifOrPng,
660 'command' => $imageProcessor->IM_commands,
661 ];
662 return $this->getImageTestResponse($result);
663 }
664
665 /**
666 * GD from image with box
667 *
668 * @return ResponseInterface
669 */
670 public function imageProcessingGdlibFromFileAction(): ResponseInterface
671 {
672 $imageProcessor = $this->initializeImageProcessor();
673 $gifOrPng = $imageProcessor->gifExtension;
674 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
675 $inputFile = $imageBasePath . 'TestInput/Test.' . $gifOrPng;
676 $image = $imageProcessor->imageCreateFromFile($inputFile);
677 $workArea = [0, 0, 400, 300];
678 $conf = [
679 'dimensions' => '10,50,380,50',
680 'color' => 'olive',
681 ];
682 $imageProcessor->makeBox($image, $conf, $workArea);
683 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdBox') . '.' . $gifOrPng;
684 $imageProcessor->ImageWrite($image, $outputFile);
685 $imResult = $imageProcessor->getImageDimensions($outputFile);
686 $result = [
687 'fileExists' => true,
688 'outputFile' => $imResult[3],
689 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Gdlib-box.' . $gifOrPng,
690 'command' => $imageProcessor->IM_commands,
691 ];
692 return $this->getImageTestResponse($result);
693 }
694
695 /**
696 * GD with text
697 *
698 * @return ResponseInterface
699 */
700 public function imageProcessingGdlibRenderTextAction(): ResponseInterface
701 {
702 $imageProcessor = $this->initializeImageProcessor();
703 $gifOrPng = $imageProcessor->gifExtension;
704 $image = imagecreatetruecolor(300, 225);
705 $backgroundColor = imagecolorallocate($image, 128, 128, 150);
706 imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
707 $workArea = [0, 0, 300, 225];
708 $conf = [
709 'iterations' => 1,
710 'angle' => 0,
711 'antiAlias' => 1,
712 'text' => 'HELLO WORLD',
713 'fontColor' => '#003366',
714 'fontSize' => 30,
715 'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
716 'offset' => '30,80',
717 ];
718 $conf['BBOX'] = $imageProcessor->calcBBox($conf);
719 $imageProcessor->makeText($image, $conf, $workArea);
720 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
721 $imageProcessor->ImageWrite($image, $outputFile);
722 $imResult = $imageProcessor->getImageDimensions($outputFile);
723 $result = [
724 'fileExists' => true,
725 'outputFile' => $imResult[3],
726 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Gdlib-text.' . $gifOrPng,
727 'command' => $imageProcessor->IM_commands,
728 ];
729 return $this->getImageTestResponse($result);
730 }
731
732 /**
733 * GD with text, niceText
734 *
735 * @return ResponseInterface
736 */
737 public function imageProcessingGdlibNiceTextAction(): ResponseInterface
738 {
739 if (!$this->isImageMagickEnabledAndConfigured()) {
740 return new JsonResponse([
741 'status' => [$this->imageMagickDisabledMessage()],
742 ]);
743 }
744 $imageProcessor = $this->initializeImageProcessor();
745 $gifOrPng = $imageProcessor->gifExtension;
746 $image = imagecreatetruecolor(300, 225);
747 $backgroundColor = imagecolorallocate($image, 128, 128, 150);
748 imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
749 $workArea = [0, 0, 300, 225];
750 $conf = [
751 'iterations' => 1,
752 'angle' => 0,
753 'antiAlias' => 1,
754 'text' => 'HELLO WORLD',
755 'fontColor' => '#003366',
756 'fontSize' => 30,
757 'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
758 'offset' => '30,80',
759 ];
760 $conf['BBOX'] = $imageProcessor->calcBBox($conf);
761 $imageProcessor->makeText($image, $conf, $workArea);
762 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
763 $imageProcessor->ImageWrite($image, $outputFile);
764 $conf['offset'] = '30,120';
765 $conf['niceText'] = 1;
766 $imageProcessor->makeText($image, $conf, $workArea);
767 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdNiceText') . '.' . $gifOrPng;
768 $imageProcessor->ImageWrite($image, $outputFile);
769 $imResult = $imageProcessor->getImageDimensions($outputFile);
770 $result = [
771 'fileExists' => true,
772 'outputFile' => $imResult[3],
773 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Gdlib-niceText.' . $gifOrPng,
774 'command' => $imageProcessor->IM_commands,
775 ];
776 return $this->getImageTestResponse($result);
777 }
778
779 /**
780 * GD with text, niceText, shadow
781 *
782 * @return ResponseInterface
783 */
784 public function imageProcessingGdlibNiceTextShadowAction(): ResponseInterface
785 {
786 if (!$this->isImageMagickEnabledAndConfigured()) {
787 return new JsonResponse([
788 'status' => [$this->imageMagickDisabledMessage()],
789 ]);
790 }
791 $imageProcessor = $this->initializeImageProcessor();
792 $gifOrPng = $imageProcessor->gifExtension;
793 $image = imagecreatetruecolor(300, 225);
794 $backgroundColor = imagecolorallocate($image, 128, 128, 150);
795 imagefilledrectangle($image, 0, 0, 300, 225, $backgroundColor);
796 $workArea = [0, 0, 300, 225];
797 $conf = [
798 'iterations' => 1,
799 'angle' => 0,
800 'antiAlias' => 1,
801 'text' => 'HELLO WORLD',
802 'fontColor' => '#003366',
803 'fontSize' => 30,
804 'fontFile' => ExtensionManagementUtility::extPath('install') . 'Resources/Private/Font/vera.ttf',
805 'offset' => '30,80',
806 ];
807 $conf['BBOX'] = $imageProcessor->calcBBox($conf);
808 $imageProcessor->makeText($image, $conf, $workArea);
809 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdText') . '.' . $gifOrPng;
810 $imageProcessor->ImageWrite($image, $outputFile);
811 $conf['offset'] = '30,120';
812 $conf['niceText'] = 1;
813 $imageProcessor->makeText($image, $conf, $workArea);
814 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('gdNiceText') . '.' . $gifOrPng;
815 $imageProcessor->ImageWrite($image, $outputFile);
816 $conf['offset'] = '30,160';
817 $conf['niceText'] = 1;
818 $conf['shadow.'] = [
819 'offset' => '2,2',
820 'blur' => '20',
821 'opacity' => '50',
822 'color' => 'black'
823 ];
824 // Warning: Re-uses $image from above!
825 $imageProcessor->makeShadow($image, $conf['shadow.'], $workArea, $conf);
826 $imageProcessor->makeText($image, $conf, $workArea);
827 $outputFile = $this->getImagesPath() . $imageProcessor->filenamePrefix . StringUtility::getUniqueId('GDwithText-niceText-shadow') . '.' . $gifOrPng;
828 $imageProcessor->ImageWrite($image, $outputFile);
829 $imResult = $imageProcessor->getImageDimensions($outputFile);
830 $result = [
831 'fileExists' => true,
832 'outputFile' => $imResult[3],
833 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Gdlib-shadow.' . $gifOrPng,
834 'command' => $imageProcessor->IM_commands,
835 ];
836 return $this->getImageTestResponse($result);
837 }
838
839 /**
840 * Initialize image processor
841 *
842 * @return GraphicalFunctions Initialized image processor
843 */
844 protected function initializeImageProcessor(): GraphicalFunctions
845 {
846 $imageProcessor = GeneralUtility::makeInstance(GraphicalFunctions::class);
847 $imageProcessor->init();
848 $imageProcessor->dontCheckForExistingTempFile = true;
849 $imageProcessor->filenamePrefix = 'installTool-';
850 $imageProcessor->dontCompress = true;
851 $imageProcessor->alternativeOutputKey = 'typo3InstallTest';
852 return $imageProcessor;
853 }
854
855 /**
856 * Determine ImageMagick / GraphicsMagick version
857 *
858 * @return string Version
859 */
860 protected function determineImageMagickVersion(): string
861 {
862 $command = CommandUtility::imageMagickCommand('identify', '-version');
863 CommandUtility::exec($command, $result);
864 $string = $result[0];
865 $version = '';
866 if (!empty($string)) {
867 list(, $version) = explode('Magick', $string);
868 list($version) = explode(' ', trim($version));
869 $version = trim($version);
870 }
871 return $version;
872 }
873
874 /**
875 * Convert to jpg from given input format
876 *
877 * @param string $inputFormat
878 * @return ResponseInterface
879 */
880 protected function convertImageFormatsToJpg(string $inputFormat): ResponseInterface
881 {
882 if (!$this->isImageMagickEnabledAndConfigured()) {
883 return new JsonResponse([
884 'status' => [$this->imageMagickDisabledMessage()],
885 ]);
886 }
887 if (!GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $inputFormat)) {
888 return new JsonResponse([
889 'status' => [
890 new FlashMessage(
891 'Handling format ' . $inputFormat . ' must be enabled in TYPO3_CONF_VARS[\'GFX\'][\'imagefile_ext\']',
892 'Skipped test',
893 FlashMessage::WARNING
894 )
895 ]
896 ]);
897 }
898 $imageBasePath = ExtensionManagementUtility::extPath('install') . 'Resources/Public/Images/';
899 $imageProcessor = $this->initializeImageProcessor();
900 $inputFile = $imageBasePath . 'TestInput/Test.' . $inputFormat;
901 $imageProcessor->imageMagickConvert_forceFileNameBody = StringUtility::getUniqueId('read') . '-' . $inputFormat;
902 $imResult = $imageProcessor->imageMagickConvert($inputFile, 'jpg', '300', '', '', '', [], true);
903 $result = [];
904 if ($imResult !== null) {
905 $result = [
906 'fileExists' => file_exists($imResult[3]),
907 'outputFile' => $imResult[3],
908 'referenceFile' => Environment::getPublicPath() . '/typo3/sysext/install/Resources/Public/Images/TestReference/Read-' . $inputFormat . '.jpg',
909 'command' => $imageProcessor->IM_commands,
910 ];
911 } else {
912 $result = [
913 'status' => [$this->imageGenerationFailedMessage()],
914 'command' => $imageProcessor->IM_commands,
915 ];
916 }
917 return $this->getImageTestResponse($result);
918 }
919
920 /**
921 * Get details about all configured database connections
922 *
923 * @return array
924 */
925 protected function getDatabaseConnectionInformation(): array
926 {
927 $connectionInfos = [];
928 $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
929 foreach ($connectionPool->getConnectionNames() as $connectionName) {
930 $connection = $connectionPool->getConnectionByName($connectionName);
931 $connectionParameters = $connection->getParams();
932 $connectionInfo = [
933 'connectionName' => $connectionName,
934 'version' => $connection->getServerVersion(),
935 'databaseName' => $connection->getDatabase(),
936 'username' => $connection->getUsername(),
937 'host' => $connection->getHost(),
938 'port' => $connection->getPort(),
939 'socket' => $connectionParameters['unix_socket'] ?? '',
940 'numberOfTables' => count($connection->getSchemaManager()->listTableNames()),
941 'numberOfMappedTables' => 0,
942 ];
943 if (isset($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
944 && is_array($GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'])
945 ) {
946 // Count number of array keys having $connectionName as value
947 $connectionInfo['numberOfMappedTables'] = count(array_intersect(
948 $GLOBALS['TYPO3_CONF_VARS']['DB']['TableMapping'],
949 [$connectionName]
950 ));
951 }
952 $connectionInfos[] = $connectionInfo;
953 }
954 return $connectionInfos;
955 }
956
957 /**
958 * Get sender address from configuration
959 * ['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']
960 * If this setting is empty fall back to 'no-reply@example.com'
961 *
962 * @return string Returns an email address
963 */
964 protected function getSenderEmailAddress(): string
965 {
966 return !empty($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress'])
967 ? $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']
968 : 'no-reply@example.com';
969 }
970
971 /**
972 * Gets sender name from configuration
973 * ['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']
974 * If this setting is empty, it falls back to a default string.
975 *
976 * @return string
977 */
978 protected function getSenderEmailName(): string
979 {
980 return !empty($GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName'])
981 ? $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']
982 : 'TYPO3 CMS install tool';
983 }
984
985 /**
986 * Gets email subject from configuration
987 * ['TYPO3_CONF_VARS']['SYS']['sitename']
988 * If this setting is empty, it falls back to a default string.
989 *
990 * @return string
991 */
992 protected function getEmailSubject(): string
993 {
994 $name = !empty($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'])
995 ? ' from site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '"'
996 : '';
997 return 'Test TYPO3 CMS mail delivery' . $name;
998 }
999
1000 /**
1001 * Create a JsonResponse from single image tests
1002 *
1003 * @param array $testResult
1004 * @return ResponseInterface
1005 */
1006 protected function getImageTestResponse(array $testResult): ResponseInterface
1007 {
1008 $responseData = [
1009 'success' => true,
1010 ];
1011 foreach ($testResult as $resultKey => $value) {
1012 if ($resultKey === 'referenceFile') {
1013 $fileExt = end(explode('.', $testResult['referenceFile']));
1014 $responseData['referenceFile'] = 'data:image/' . $fileExt . ';base64,' . base64_encode(file_get_contents($testResult['referenceFile']));
1015 } elseif ($resultKey === 'outputFile') {
1016 $fileExt = end(explode('.', $testResult['outputFile']));
1017 $responseData['outputFile'] = 'data:image/' . $fileExt . ';base64,' . base64_encode(file_get_contents($testResult['outputFile']));
1018 } else {
1019 $responseData[$resultKey] = $value;
1020 }
1021 }
1022 return new JsonResponse($responseData);
1023 }
1024
1025 /**
1026 * Create a 'image generation failed' message
1027 *
1028 * @return FlashMessage
1029 */
1030 protected function imageGenerationFailedMessage(): FlashMessage
1031 {
1032 return new FlashMessage(
1033 'ImageMagick / GraphicsMagick handling is enabled, but the execute'
1034 . ' command returned an error. Please check your settings, especially'
1035 . ' [\'GFX\'][\'processor_path\'] and [\'GFX\'][\'processor_path_lzw\'] and ensure Ghostscript is installed on your server.',
1036 'Image generation failed',
1037 FlashMessage::ERROR
1038 );
1039 }
1040
1041 /**
1042 * Find out if ImageMagick or GraphicsMagick is enabled and set up
1043 *
1044 * @return bool TRUE if enabled and path is set
1045 */
1046 protected function isImageMagickEnabledAndConfigured(): bool
1047 {
1048 $enabled = $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_enabled'];
1049 $path = $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_path'];
1050 return $enabled && $path;
1051 }
1052
1053 /**
1054 * Create a 'imageMagick disabled' message
1055 *
1056 * @return FlashMessage
1057 */
1058 protected function imageMagickDisabledMessage(): FlashMessage
1059 {
1060 return new FlashMessage(
1061 'ImageMagick / GraphicsMagick handling is disabled or not configured correctly.',
1062 'Tests not executed',
1063 FlashMessage::ERROR
1064 );
1065 }
1066
1067 /**
1068 * Return the temp image dir.
1069 * If not exist it will be created
1070 *
1071 * @return string
1072 */
1073 protected function getImagesPath(): string
1074 {
1075 $imagePath = Environment::getPublicPath() . '/typo3temp/assets/images/';
1076 if (!is_dir($imagePath)) {
1077 GeneralUtility::mkdir_deep($imagePath);
1078 }
1079 return $imagePath;
1080 }
1081 }