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