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