TYPO3 CMS  TYPO3_7-6
IconFactory.php
Go to the documentation of this file.
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 
27 
33 {
37  protected $iconRegistry;
38 
45  protected $recordStatusMapping = [];
46 
53  protected $overlayPriorities = [];
54 
58  public function __construct(IconRegistry $iconRegistry = null)
59  {
60  $this->iconRegistry = $iconRegistry ? $iconRegistry : GeneralUtility::makeInstance(IconRegistry::class);
61  $this->recordStatusMapping = $GLOBALS['TYPO3_CONF_VARS']['SYS']['IconFactory']['recordStatusMapping'];
62  $this->overlayPriorities = $GLOBALS['TYPO3_CONF_VARS']['SYS']['IconFactory']['overlayPriorities'];
63  }
64 
71  public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response)
72  {
73  $parsedBody = $request->getParsedBody();
74  $queryParams = $request->getQueryParams();
75  $requestedIcon = json_decode(
76  isset($parsedBody['icon']) ? $parsedBody['icon'] : $queryParams['icon'],
77  true
78  );
79 
80  list($identifier, $size, $overlayIdentifier, $iconState) = $requestedIcon;
81  if (empty($overlayIdentifier)) {
82  $overlayIdentifier = null;
83  }
84  $iconState = IconState::cast($iconState);
85  $response->getBody()->write(
86  $this->getIcon($identifier, $size, $overlayIdentifier, $iconState)->render()
87  );
88  $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
89  return $response;
90  }
91 
99  public function getIcon($identifier, $size = Icon::SIZE_DEFAULT, $overlayIdentifier = null, IconState $state = null)
100  {
101  if ($this->iconRegistry->isDeprecated($identifier)) {
102  $deprecationSettings = $this->iconRegistry->getDeprecationSettings($identifier);
103  GeneralUtility::deprecationLog(sprintf($deprecationSettings['message'], $identifier));
104  if (!empty($deprecationSettings['replacement'])) {
105  $identifier = $deprecationSettings['replacement'];
106  }
107  }
108  if (!$this->iconRegistry->isRegistered($identifier)) {
109  $identifier = $this->iconRegistry->getDefaultIconIdentifier();
110  }
111 
112  $iconConfiguration = $this->iconRegistry->getIconConfigurationByIdentifier($identifier);
113  $iconConfiguration['state'] = $state;
114  $icon = $this->createIcon($identifier, $size, $overlayIdentifier, $iconConfiguration);
115 
117  $iconProvider = GeneralUtility::makeInstance($iconConfiguration['provider']);
118  $iconProvider->prepareIconMarkup($icon, $iconConfiguration['options']);
119 
120  return $icon;
121  }
122 
131  public function getIconForRecord($table, array $row, $size = Icon::SIZE_DEFAULT)
132  {
133  $iconIdentifier = $this->mapRecordTypeToIconIdentifier($table, $row);
134  $overlayIdentifier = $this->mapRecordTypeToOverlayIdentifier($table, $row);
135  if (empty($overlayIdentifier)) {
136  $overlayIdentifier = null;
137  }
138  return $this->getIcon($iconIdentifier, $size, $overlayIdentifier);
139  }
140 
157  public function mapRecordTypeToIconIdentifier($table, array $row)
158  {
159  $recordType = [];
160  $ref = null;
161 
162  if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_column'])) {
163  $column = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
164  if (isset($row[$column])) {
165  // even if not properly documented the value of the typeicon_column in a record could be
166  // an array (multiselect) in typeicon_classes a key could consist of a comma-separated string "foo,bar"
167  // but mostly it should be only one entry in that array
168  if (is_array($row[$column])) {
169  $recordType[1] = implode(',', $row[$column]);
170  } else {
171  $recordType[1] = $row[$column];
172  }
173  } else {
174  $recordType[1] = 'default';
175  }
176  // Workaround to give nav_hide pages a complete different icon
177  // Although it's not a separate doctype
178  // and to give root-pages an own icon
179  if ($table === 'pages') {
180  if ((int)$row['nav_hide'] > 0) {
181  $recordType[2] = $recordType[1] . '-hideinmenu';
182  }
183  if ((int)$row['is_siteroot'] > 0) {
184  $recordType[3] = $recordType[1] . '-root';
185  }
186  if (!empty($row['module'])) {
187  $recordType[4] = 'contains-' . $row['module'];
188  }
189  if ((int)$row['content_from_pid'] > 0) {
190  if ($row['is_siteroot']) {
191  $recordType[4] = 'page-contentFromPid-root';
192  } else {
193  $recordType[4] = (int)$row['nav_hide'] === 0
194  ? 'page-contentFromPid' : 'page-contentFromPid-hideinmenu';
195  }
196  }
197  }
198  if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])
199  && is_array($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])
200  ) {
201  foreach ($recordType as $key => $type) {
202  if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'][$type])) {
203  $recordType[$key] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'][$type];
204  } else {
205  unset($recordType[$key]);
206  }
207  }
208  $recordType[0] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['default'];
209  if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['mask'])) {
210  $recordType[5] = str_replace(
211  '###TYPE###',
212  $row[$column],
213  $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['mask']
214  );
215  }
216  if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['userFunc'])) {
217  $parameters = ['row' => $row];
218  $recordType[6] = GeneralUtility::callUserFunction(
219  $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['userFunc'],
220  $parameters,
221  $ref
222  );
223  }
224  } else {
225  foreach ($recordType as &$type) {
226  $type = 'tcarecords-' . $table . '-' . $type;
227  }
228  unset($type);
229  $recordType[0] = 'tcarecords-' . $table . '-default';
230  }
231  } elseif (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])
232  && is_array($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])
233  ) {
234  $recordType[0] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['default'];
235  } else {
236  $recordType[0] = 'tcarecords-' . $table . '-default';
237  }
238 
239  krsort($recordType);
240  foreach ($recordType as $iconName) {
241  if ($this->iconRegistry->isRegistered($iconName)) {
242  return $iconName;
243  }
244  }
245 
246  return $this->iconRegistry->getDefaultIconIdentifier();
247  }
248 
258  protected function mapRecordTypeToOverlayIdentifier($table, array $row)
259  {
260  $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
261  // Calculate for a given record the actual visibility at the moment
262  $status = [
263  'hidden' => false,
264  'starttime' => false,
265  'endtime' => false,
266  'futureendtime' => false,
267  'fe_group' => false,
268  'deleted' => false,
269  'protectedSection' => false,
270  'nav_hide' => !empty($row['nav_hide']),
271  ];
272  // Icon state based on "enableFields":
273  if (isset($tcaCtrl['enablecolumns']) && is_array($tcaCtrl['enablecolumns'])) {
274  $enableColumns = $tcaCtrl['enablecolumns'];
275  // If "hidden" is enabled:
276  if (isset($enableColumns['disabled']) && !empty($row[$enableColumns['disabled']])) {
277  $status['hidden'] = true;
278  }
279  // If a "starttime" is set and higher than current time:
280  if (!empty($enableColumns['starttime']) && $GLOBALS['EXEC_TIME'] < (int)$row[$enableColumns['starttime']]) {
281  $status['starttime'] = true;
282  }
283  // If an "endtime" is set
284  if (!empty($enableColumns['endtime'])) {
285  if ((int)$row[$enableColumns['endtime']] > 0) {
286  if ((int)$row[$enableColumns['endtime']] < $GLOBALS['EXEC_TIME']) {
287  // End-timing applies at this point.
288  $status['endtime'] = true;
289  } else {
290  // End-timing WILL apply in the future for this element.
291  $status['futureendtime'] = true;
292  }
293  }
294  }
295  // If a user-group field is set
296  if (!empty($enableColumns['fe_group']) && $row[$enableColumns['fe_group']]) {
297  $status['fe_group'] = true;
298  }
299  }
300  // If "deleted" flag is set (only when listing records which are also deleted!)
301  if (isset($tcaCtrl['delete']) && !empty($row[$tcaCtrl['delete']])) {
302  $status['deleted'] = true;
303  }
304  // Detecting extendToSubpages (for pages only)
305  if ($table === 'pages' && (int)$row['extendToSubpages'] > 0) {
306  $status['protectedSection'] = true;
307  }
308  if (isset($row['t3ver_state'])
309  && VersionState::cast($row['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
310  $status['deleted'] = true;
311  }
312 
313  // Now only show the status with the highest priority
314  $iconName = '';
315  foreach ($this->overlayPriorities as $priority) {
316  if ($status[$priority]) {
317  $iconName = $this->recordStatusMapping[$priority];
318  break;
319  }
320  }
321 
322  // Hook to define an alternative iconName
323  if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['overrideIconOverlay'])) {
324  $hookObjects = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['overrideIconOverlay'];
325  foreach ($hookObjects as $classRef) {
326  $hookObject = GeneralUtility::getUserObj($classRef);
327  if (method_exists($hookObject, 'postOverlayPriorityLookup')) {
328  $iconName = $hookObject->postOverlayPriorityLookup($table, $row, $status, $iconName);
329  }
330  }
331  }
332 
333  return $iconName;
334  }
335 
344  public function getIconForFileExtension($fileExtension, $size = Icon::SIZE_DEFAULT, $overlayIdentifier = null)
345  {
346  $iconName = $this->iconRegistry->getIconIdentifierForFileExtension($fileExtension);
347  return $this->getIcon($iconName, $size, $overlayIdentifier);
348  }
349 
368  public function getIconForResource(
369  ResourceInterface $resource,
370  $size = Icon::SIZE_DEFAULT,
371  $overlayIdentifier = null,
372  array $options = []
373  ) {
374  $iconIdentifier = null;
375 
376  // Folder
377  if ($resource instanceof FolderInterface) {
378  // non browsable storage
379  if ($resource->getStorage()->isBrowsable() === false && !empty($options['mount-root'])) {
380  $iconIdentifier = 'apps-filetree-folder-locked';
381  } else {
382  // storage root
383  if ($resource->getStorage()->getRootLevelFolder()->getIdentifier() === $resource->getIdentifier()) {
384  $iconIdentifier = 'apps-filetree-root';
385  }
386 
387  $role = is_callable([$resource, 'getRole']) ? $resource->getRole() : '';
388 
389  // user/group mount root
390  if (!empty($options['mount-root'])) {
391  $iconIdentifier = 'apps-filetree-mount';
392  if ($role === FolderInterface::ROLE_READONLY_MOUNT) {
393  $overlayIdentifier = 'overlay-locked';
394  } elseif ($role === FolderInterface::ROLE_USER_MOUNT) {
395  $overlayIdentifier = 'overlay-restricted';
396  }
397  }
398 
399  if ($iconIdentifier === null) {
400  // in folder tree view $options['folder-open'] can define an open folder icon
401  if (!empty($options['folder-open'])) {
402  $iconIdentifier = 'apps-filetree-folder-opened';
403  } else {
404  $iconIdentifier = 'apps-filetree-folder-default';
405  }
406 
407  if ($role === FolderInterface::ROLE_TEMPORARY) {
408  $iconIdentifier = 'apps-filetree-folder-temp';
409  } elseif ($role === FolderInterface::ROLE_RECYCLER) {
410  $iconIdentifier = 'apps-filetree-folder-recycler';
411  }
412  }
413 
414  // if locked add overlay
415  if ($resource instanceof InaccessibleFolder ||
416  !$resource->getStorage()->isBrowsable() ||
417  !$resource->getStorage()->checkFolderActionPermission('add', $resource)
418  ) {
419  $overlayIdentifier = 'overlay-locked';
420  }
421  }
422 
423  // File
424  } elseif ($resource instanceof File) {
425  $mimeTypeIcon = $this->iconRegistry->getIconIdentifierForMimeType($resource->getMimeType());
426 
427  // Check if we find a exact matching mime type
428  if ($mimeTypeIcon !== null) {
429  $iconIdentifier = $mimeTypeIcon;
430  } else {
431  $fileExtensionIcon = $this->iconRegistry->getIconIdentifierForFileExtension($resource->getExtension());
432  if ($fileExtensionIcon !== 'mimetypes-other-other') {
433  // Fallback 1: icon by file extension
434  $iconIdentifier = $fileExtensionIcon;
435  } else {
436  // Fallback 2: icon by mime type with subtype replaced by *
437  $mimeTypeParts = explode('/', $resource->getMimeType());
438  $mimeTypeIcon = $this->iconRegistry->getIconIdentifierForMimeType($mimeTypeParts[0] . '/*');
439  if ($mimeTypeIcon !== null) {
440  $iconIdentifier = $mimeTypeIcon;
441  } else {
442  // Fallback 3: use 'mimetypes-other-other'
443  $iconIdentifier = $fileExtensionIcon;
444  }
445  }
446  }
447  if ($resource->isMissing()) {
448  $overlayIdentifier = 'overlay-missing';
449  }
450  }
451 
452  unset($options['mount-root']);
453  unset($options['folder-open']);
454  list($iconIdentifier, $overlayIdentifier) =
455  $this->emitBuildIconForResourceSignal($resource, $size, $options, $iconIdentifier, $overlayIdentifier);
456  return $this->getIcon($iconIdentifier, $size, $overlayIdentifier);
457  }
458 
468  protected function createIcon($identifier, $size, $overlayIdentifier = null, array $iconConfiguration = [])
469  {
470  $icon = GeneralUtility::makeInstance(Icon::class);
471  $icon->setIdentifier($identifier);
472  $icon->setSize($size);
473  $icon->setState($iconConfiguration['state'] ?: new IconState());
474  if ($overlayIdentifier !== null) {
475  $icon->setOverlayIcon($this->getIcon($overlayIdentifier, Icon::SIZE_OVERLAY));
476  }
477  if (!empty($iconConfiguration['options']['spinning'])) {
478  $icon->setSpinning(true);
479  }
480 
481  return $icon;
482  }
483 
496  protected function emitBuildIconForResourceSignal(
497  ResourceInterface $resource,
498  $size,
499  array $options,
500  $iconIdentifier,
501  $overlayIdentifier
502  ) {
503  $result = $this->getSignalSlotDispatcher()->dispatch(
504  self::class,
505  'buildIconForResourceSignal',
506  [$resource, $size, $options, $iconIdentifier, $overlayIdentifier]
507  );
508  $iconIdentifier = $result[3];
509  $overlayIdentifier = $result[4];
510  return [$iconIdentifier, $overlayIdentifier];
511  }
512 
518  protected function getSignalSlotDispatcher()
519  {
520  return GeneralUtility::makeInstance(Dispatcher::class);
521  }
522 }
__construct(IconRegistry $iconRegistry=null)
Definition: IconFactory.php:58
createIcon($identifier, $size, $overlayIdentifier=null, array $iconConfiguration=[])
processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response)
Definition: IconFactory.php:71
mapRecordTypeToOverlayIdentifier($table, array $row)
getIconForRecord($table, array $row, $size=Icon::SIZE_DEFAULT)
getIconForFileExtension($fileExtension, $size=Icon::SIZE_DEFAULT, $overlayIdentifier=null)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
getIconForResource(ResourceInterface $resource, $size=Icon::SIZE_DEFAULT, $overlayIdentifier=null, array $options=[])
mapRecordTypeToIconIdentifier($table, array $row)
emitBuildIconForResourceSignal(ResourceInterface $resource, $size, array $options, $iconIdentifier, $overlayIdentifier)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']