[TASK] Render Resources using SVG in IconFactory
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Imaging / IconFactory.php
1 <?php
2 namespace TYPO3\CMS\Core\Imaging;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Type\Icon\IconState;
18 use TYPO3\CMS\Core\Resource\File;
19 use TYPO3\CMS\Core\Resource\FolderInterface;
20 use TYPO3\CMS\Core\Resource\InaccessibleFolder;
21 use TYPO3\CMS\Core\Resource\ResourceInterface;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23 use TYPO3\CMS\Core\Versioning\VersionState;
24 use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
25
26 /**
27 * The main factory class, which acts as the entrypoint for generating an Icon object which
28 * is responsible for rendering an icon. Checks for the correct icon provider through the IconRegistry.
29 */
30 class IconFactory {
31
32 /**
33 * @var IconRegistry
34 */
35 protected $iconRegistry;
36
37 /**
38 * @var string[]
39 */
40 protected $fileExtensionMapping = array(
41 'htm' => 'mimetypes-text-html',
42 'html' => 'mimetypes-text-html',
43 'css' => 'mimetypes-text-css',
44 'js' => 'mimetypes-text-js',
45 'csv' => 'mimetypes-text-csv',
46 'php' => 'mimetypes-text-php',
47 'php6' => 'mimetypes-text-php',
48 'php5' => 'mimetypes-text-php',
49 'php4' => 'mimetypes-text-php',
50 'php3' => 'mimetypes-text-php',
51 'inc' => 'mimetypes-text-php',
52 'ts' => 'mimetypes-text-ts',
53 'txt' => 'mimetypes-text-text',
54 'class' => 'mimetypes-text-text',
55 'tmpl' => 'mimetypes-text-text',
56 'jpg' => 'mimetypes-media-image',
57 'jpeg' => 'mimetypes-media-image',
58 'gif' => 'mimetypes-media-image',
59 'png' => 'mimetypes-media-image',
60 'bmp' => 'mimetypes-media-image',
61 'tif' => 'mimetypes-media-image',
62 'tiff' => 'mimetypes-media-image',
63 'tga' => 'mimetypes-media-image',
64 'psd' => 'mimetypes-media-image',
65 'eps' => 'mimetypes-media-image',
66 'ai' => 'mimetypes-media-image',
67 'svg' => 'mimetypes-media-image',
68 'pcx' => 'mimetypes-media-image',
69 'avi' => 'mimetypes-media-video',
70 'mpg' => 'mimetypes-media-video',
71 'mpeg' => 'mimetypes-media-video',
72 'mov' => 'mimetypes-media-video',
73 'wav' => 'mimetypes-media-audio',
74 'mp3' => 'mimetypes-media-audio',
75 'mid' => 'mimetypes-media-audio',
76 'swf' => 'mimetypes-media-flash',
77 'swa' => 'mimetypes-media-flash',
78 'exe' => 'mimetypes-executable-executable',
79 'com' => 'mimetypes-executable-executable',
80 't3x' => 'mimetypes-compressed',
81 't3d' => 'mimetypes-compressed',
82 'zip' => 'mimetypes-compressed',
83 'tgz' => 'mimetypes-compressed',
84 'gz' => 'mimetypes-compressed',
85 'pdf' => 'mimetypes-pdf',
86 'doc' => 'mimetypes-word',
87 'dot' => 'mimetypes-word',
88 'docm' => 'mimetypes-word',
89 'docx' => 'mimetypes-word',
90 'dotm' => 'mimetypes-word',
91 'dotx' => 'mimetypes-word',
92 'sxw' => 'mimetypes-word',
93 'rtf' => 'mimetypes-word',
94 'xls' => 'mimetypes-excel',
95 'xlsm' => 'mimetypes-excel',
96 'xlsx' => 'mimetypes-excel',
97 'xltm' => 'mimetypes-excel',
98 'xltx' => 'mimetypes-excel',
99 'sxc' => 'mimetypes-excel',
100 'pps' => 'mimetypes-powerpoint',
101 'ppsx' => 'mimetypes-powerpoint',
102 'ppt' => 'mimetypes-powerpoint',
103 'pptm' => 'mimetypes-powerpoint',
104 'pptx' => 'mimetypes-powerpoint',
105 'potm' => 'mimetypes-powerpoint',
106 'potx' => 'mimetypes-powerpoint',
107 'mount' => 'apps-filetree-mount',
108 'folder' => 'apps-filetree-folder-default',
109 'default' => 'mimetypes-other-other',
110 );
111
112 /**
113 * @param IconRegistry $iconRegistry
114 */
115 public function __construct(IconRegistry $iconRegistry = NULL) {
116 $this->iconRegistry = $iconRegistry ? $iconRegistry : GeneralUtility::makeInstance(IconRegistry::class);
117 }
118
119 /**
120 * @param string $identifier
121 * @param string $size "large", "small" or "default", see the constants of the Icon class
122 * @param string $overlayIdentifier
123 * @param IconState $state
124 * @return Icon
125 */
126 public function getIcon($identifier, $size = Icon::SIZE_DEFAULT, $overlayIdentifier = NULL, IconState $state = NULL) {
127 if ($this->iconRegistry->isDeprecated($identifier)) {
128 $deprecationSettings = $this->iconRegistry->getDeprecationSettings($identifier);
129 GeneralUtility::deprecationLog(sprintf($deprecationSettings['message'], $identifier));
130 if (!empty($deprecationSettings['replacement'])) {
131 $identifier = $deprecationSettings['replacement'];
132 }
133 }
134 if (!$this->iconRegistry->isRegistered($identifier)) {
135 $identifier = $this->iconRegistry->getDefaultIconIdentifier();
136 }
137
138 $iconConfiguration = $this->iconRegistry->getIconConfigurationByIdentifier($identifier);
139 $iconConfiguration['state'] = $state;
140 $icon = $this->createIcon($identifier, $size, $overlayIdentifier, $iconConfiguration);
141 /** @var IconProviderInterface $iconProvider */
142 $iconProvider = GeneralUtility::makeInstance($iconConfiguration['provider']);
143 $iconProvider->prepareIconMarkup($icon, $iconConfiguration['options']);
144
145 return $icon;
146 }
147
148 /**
149 * Get Icon for a file by its extension
150 *
151 * @param string $fileExtension
152 * @param string $size "large" "small" or "default", see the constants of the Icon class
153 * @param string $overlayIdentifier
154 * @return Icon
155 */
156 public function getIconForFileExtension($fileExtension, $size = Icon::SIZE_DEFAULT, $overlayIdentifier = NULL) {
157 $iconName = $this->getIconIdentifierForFileExtension($fileExtension);
158 return $this->getIcon($iconName, $size, $overlayIdentifier);
159 }
160
161 /**
162 * @param string $fileExtension
163 *
164 * @return string
165 */
166 protected function getIconIdentifierForFileExtension($fileExtension) {
167 // If the file extension is not valid use the default one
168 if (!isset($this->fileExtensionMapping[$fileExtension])) {
169 $fileExtension = 'default';
170 }
171 return $this->fileExtensionMapping[$fileExtension];
172 }
173
174 /**
175 * This method is used throughout the TYPO3 Backend to show icons for files and folders
176 *
177 * The method takes care of the translation of file extension to proper icon and for folders
178 * it will return the icon depending on the role of the folder.
179 *
180 * If the given resource is a folder there are some additional options that can be used:
181 * - mount-root => TRUE (to indicate this is the root of a mount)
182 * - folder-open => TRUE (to indicate that the folder is opened in the file tree)
183 *
184 * There is a hook in place to manipulate the icon name and overlays.
185 *
186 * @param ResourceInterface $resource
187 * @param string $size "large" "small" or "default", see the constants of the Icon class
188 * @param string $overlayIdentifier
189 * @param array $options An associative array with additional options.
190 * @return Icon
191 */
192 public function getIconForResource(ResourceInterface $resource, $size = Icon::SIZE_DEFAULT, $overlayIdentifier = NULL, array $options = array()) {
193 $iconIdentifier = NULL;
194
195 // Folder
196 if ($resource instanceof FolderInterface) {
197 // non browsable storage
198 if ($resource->getStorage()->isBrowsable() === FALSE && !empty($options['mount-root'])) {
199 $iconIdentifier = 'apps-filetree-folder-locked';
200 } else {
201 // storage root
202 if ($resource->getStorage()->getRootLevelFolder()->getIdentifier() === $resource->getIdentifier()) {
203 $iconIdentifier = 'apps-filetree-root';
204 }
205
206 $role = is_callable([$resource, 'getRole']) ? $resource->getRole() : '';
207
208 // user/group mount root
209 if (!empty($options['mount-root'])) {
210 $iconIdentifier = 'apps-filetree-mount';
211 if ($role === FolderInterface::ROLE_READONLY_MOUNT) {
212 $overlayIdentifier = 'overlay-locked';
213 } elseif ($role === FolderInterface::ROLE_USER_MOUNT) {
214 $overlayIdentifier = 'overlay-restricted';
215 }
216 }
217
218 if ($iconIdentifier === NULL) {
219 // in folder tree view $options['folder-open'] can define an open folder icon
220 if (!empty($options['folder-open'])) {
221 $iconIdentifier = 'apps-filetree-folder-opened';
222 } else {
223 $iconIdentifier = 'apps-filetree-folder-default';
224 }
225
226 if ($role === FolderInterface::ROLE_TEMPORARY) {
227 $iconIdentifier = 'apps-filetree-folder-temp';
228 } elseif ($role === FolderInterface::ROLE_RECYCLER) {
229 $iconIdentifier = 'apps-filetree-folder-recycler';
230 }
231 }
232
233 // if locked add overlay
234 if ($resource instanceof InaccessibleFolder ||
235 !$resource->getStorage()->isBrowsable() ||
236 !$resource->getStorage()->checkFolderActionPermission('add', $resource)
237 ) {
238 $overlayIdentifier = 'overlay-locked';
239 }
240 }
241
242 // File
243 } else {
244 if ($resource instanceof File && $resource->isMissing()) {
245 $overlayIdentifier = 'overlay-missing';
246 }
247 $iconIdentifier = $this->getIconIdentifierForFileExtension($resource->getExtension());
248 }
249
250 unset($options['mount-root']);
251 unset($options['folder-open']);
252 list($iconIdentifier, $overlayIdentifier) = $this->emitBuildIconForResourceSignal($resource, $size, $options, $iconIdentifier, $overlayIdentifier);
253 return $this->getIcon($iconIdentifier, $size, $overlayIdentifier);
254 }
255
256 /**
257 * Creates an icon object
258 *
259 * @param string $identifier
260 * @param string $size "large", "small" or "default", see the constants of the Icon class
261 * @param string $overlayIdentifier
262 * @param array $iconConfiguration the icon configuration array
263 * @return Icon
264 */
265 protected function createIcon($identifier, $size, $overlayIdentifier = NULL, array $iconConfiguration) {
266 $icon = GeneralUtility::makeInstance(Icon::class);
267 $icon->setIdentifier($identifier);
268 $icon->setSize($size);
269 $icon->setState($iconConfiguration['state'] ?: new IconState());
270 if ($overlayIdentifier !== NULL) {
271 $icon->setOverlayIcon($this->getIcon($overlayIdentifier, Icon::SIZE_OVERLAY));
272 }
273 if (!empty($iconConfiguration['options']['spinning'])) {
274 $icon->setSpinning(TRUE);
275 }
276
277 return $icon;
278 }
279
280 /**
281 * Emits a signal right after the identifiers are built.
282 *
283 * @param ResourceInterface $resource
284 * @param string $size
285 * @param array $options
286 * @param string $iconIdentifier
287 * @param string $overlayIdentifier
288 *
289 * @return mixed
290 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotException
291 * @throws \TYPO3\CMS\Extbase\SignalSlot\Exception\InvalidSlotReturnException
292 */
293 protected function emitBuildIconForResourceSignal(ResourceInterface $resource, $size, array $options, $iconIdentifier, $overlayIdentifier) {
294 $result = $this->getSignalSlotDispatcher()->dispatch(IconFactory::class, 'buildIconForResourceSignal', array($resource, $size, $options, $iconIdentifier, $overlayIdentifier));
295 $iconIdentifier = $result[3];
296 $overlayIdentifier = $result[4];
297 return array($iconIdentifier, $overlayIdentifier);
298 }
299
300 /**
301 * Get the SignalSlot dispatcher
302 *
303 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
304 */
305 protected function getSignalSlotDispatcher() {
306 return GeneralUtility::makeInstance(Dispatcher::class);
307 }
308
309 }