Always create thumbnails using WebP
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / file / processor / FileProcessor.class.php
1 <?php
2
3 namespace wcf\system\file\processor;
4
5 use wcf\data\file\File;
6 use wcf\data\file\thumbnail\FileThumbnail;
7 use wcf\data\file\thumbnail\FileThumbnailEditor;
8 use wcf\data\file\thumbnail\FileThumbnailList;
9 use wcf\system\event\EventHandler;
10 use wcf\system\file\processor\event\FileProcessorCollecting;
11 use wcf\system\image\adapter\ImageAdapter;
12 use wcf\system\image\ImageHandler;
13 use wcf\system\SingletonFactory;
14 use wcf\util\FileUtil;
15 use wcf\util\JSON;
16 use wcf\util\StringUtil;
17
18 /**
19 * @author Alexander Ebert
20 * @copyright 2001-2024 WoltLab GmbH
21 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
22 * @since 6.1
23 */
24 final class FileProcessor extends SingletonFactory
25 {
26 /**
27 * @var array<string, IFileProcessor>
28 */
29 private array $processors;
30
31 #[\Override]
32 public function init(): void
33 {
34 $event = new FileProcessorCollecting();
35 EventHandler::getInstance()->fire($event);
36 $this->processors = $event->getProcessors();
37 }
38
39 public function forTypeName(string $typeName): ?IFileProcessor
40 {
41 return $this->processors[$typeName] ?? null;
42 }
43
44 public function getHtmlElement(IFileProcessor $fileProcessor, array $context): string
45 {
46 $allowedFileExtensions = $fileProcessor->getAllowedFileExtensions($context);
47 if (\in_array('*', $allowedFileExtensions)) {
48 $allowedFileExtensions = '';
49 } else {
50 $allowedFileExtensions = \implode(
51 ',',
52 \array_map(
53 static fn (string $fileExtension) => ".{$fileExtension}",
54 $allowedFileExtensions
55 )
56 );
57 }
58
59 return \sprintf(
60 <<<'HTML'
61 <woltlab-core-file-upload
62 data-type-name="%s"
63 data-context="%s"
64 data-file-extensions="%s"
65 ></woltlab-core-file-upload>
66 HTML,
67 StringUtil::encodeHTML($fileProcessor->getTypeName()),
68 StringUtil::encodeHTML(JSON::encode($context)),
69 StringUtil::encodeHTML($allowedFileExtensions),
70 );
71 }
72
73 public function generateThumbnails(File $file): void
74 {
75 $processor = $file->getProcessor();
76 if ($processor === null) {
77 return;
78 }
79
80 $formats = $processor->getThumbnailFormats();
81 if ($formats === []) {
82 return;
83 }
84
85 $thumbnailList = new FileThumbnailList();
86 $thumbnailList->getConditionBuilder()->add("fileID = ?", [$file->fileID]);
87 $thumbnailList->readObjects();
88
89 $existingThumbnails = [];
90 foreach ($thumbnailList as $thumbnail) {
91 \assert($thumbnail instanceof FileThumbnail);
92 $existingThumbnails[$thumbnail->identifier] = $thumbnail;
93 }
94
95 $imageAdapter = null;
96 foreach ($formats as $format) {
97 if (isset($existingThumbnails[$format->identifier])) {
98 continue;
99 }
100
101 if ($imageAdapter === null) {
102 $imageAdapter = ImageHandler::getInstance()->getAdapter();
103 $imageAdapter->loadFile($file->getPath() . $file->getSourceFilename());
104 }
105
106 \assert($imageAdapter instanceof ImageAdapter);
107 $image = $imageAdapter->createThumbnail($format->width, $format->height, $format->retainDimensions);
108
109 $filename = FileUtil::getTemporaryFilename(extension: 'webp');
110 $imageAdapter->saveImageAs($image, $filename, 'webp', 80);
111
112 $fileThumbnail = FileThumbnailEditor::createFromTemporaryFile($file, $format, $filename);
113 $processor->adoptThumbnail($fileThumbnail);
114 }
115 }
116 }