13ca03ffc7da95720b0b2d4f0d2fe00499b866b1
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Utility / IconUtility.php
1 <?php
2 namespace TYPO3\CMS\Backend\Utility;
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\Utility\GeneralUtility;
18 use TYPO3\CMS\Core\Versioning\VersionState;
19
20 /**
21 * Contains class for icon generation in the backend
22 * This library has functions that returns - and if necessary creates - the icon for an element in TYPO3
23 *
24 * Expects global vars:
25 * - $BACK_PATH
26 * - PATH_typo3
27 * - $TCA, $PAGES_TYPES
28 *
29 *
30 * Notes:
31 * These functions are strongly related to the interface of TYPO3.
32 * The class is included in eg. init.php
33 * Static class, functions called without making a class instance.
34 *
35 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
36 */
37 class IconUtility {
38
39 /**
40 * @var array
41 */
42 static public $fileSpriteIconNames = array(
43 'htm' => 'mimetypes-text-html',
44 'html' => 'mimetypes-text-html',
45 'css' => 'mimetypes-text-css',
46 'js' => 'mimetypes-text-js',
47 'csv' => 'mimetypes-text-csv',
48 'php' => 'mimetypes-text-php',
49 'php6' => 'mimetypes-text-php',
50 'php5' => 'mimetypes-text-php',
51 'php4' => 'mimetypes-text-php',
52 'php3' => 'mimetypes-text-php',
53 'inc' => 'mimetypes-text-php',
54 'ts' => 'mimetypes-text-ts',
55 'txt' => 'mimetypes-text-text',
56 'class' => 'mimetypes-text-text',
57 'tmpl' => 'mimetypes-text-text',
58 'jpg' => 'mimetypes-media-image',
59 'jpeg' => 'mimetypes-media-image',
60 'gif' => 'mimetypes-media-image',
61 'png' => 'mimetypes-media-image',
62 'bmp' => 'mimetypes-media-image',
63 'tif' => 'mimetypes-media-image',
64 'tga' => 'mimetypes-media-image',
65 'psd' => 'mimetypes-media-image',
66 'eps' => 'mimetypes-media-image',
67 'avi' => 'mimetypes-media-video',
68 'mpg' => 'mimetypes-media-video',
69 'mpeg' => 'mimetypes-media-video',
70 'mov' => 'mimetypes-media-video',
71 'wav' => 'mimetypes-media-audio',
72 'mp3' => 'mimetypes-media-audio',
73 'mid' => 'mimetypes-media-audio',
74 'swf' => 'mimetypes-media-flash',
75 'swa' => 'mimetypes-media-flash',
76 'exe' => 'mimetypes-executable-executable',
77 'com' => 'mimetypes-executable-executable',
78 't3x' => 'mimetypes-compressed',
79 't3d' => 'mimetypes-compressed',
80 'zip' => 'mimetypes-compressed',
81 'tgz' => 'mimetypes-compressed',
82 'gz' => 'mimetypes-compressed',
83 'pdf' => 'mimetypes-pdf',
84 'doc' => 'mimetypes-word',
85 'dot' => 'mimetypes-word',
86 'docm' => 'mimetypes-word',
87 'docx' => 'mimetypes-word',
88 'dotm' => 'mimetypes-word',
89 'dotx' => 'mimetypes-word',
90 'sxw' => 'mimetypes-word',
91 'rtf' => 'mimetypes-word',
92 'xls' => 'mimetypes-excel',
93 'xlsm' => 'mimetypes-excel',
94 'xlsx' => 'mimetypes-excel',
95 'xltm' => 'mimetypes-excel',
96 'xltx' => 'mimetypes-excel',
97 'sxc' => 'mimetypes-excel',
98 'pps' => 'mimetypes-powerpoint',
99 'ppsx' => 'mimetypes-powerpoint',
100 'ppt' => 'mimetypes-powerpoint',
101 'pptm' => 'mimetypes-powerpoint',
102 'pptx' => 'mimetypes-powerpoint',
103 'potm' => 'mimetypes-powerpoint',
104 'potx' => 'mimetypes-powerpoint',
105 'mount' => 'apps-filetree-mount',
106 'folder' => 'apps-filetree-folder-default',
107 'default' => 'mimetypes-other-other'
108 );
109
110 /**
111 * Array of icons rendered by getSpriteIcon(). This contains only icons
112 * without overlays or options. These are the most common form.
113 *
114 * @var array
115 */
116 static protected $spriteIconCache = array();
117
118 /**
119 * Creates the icon for input table/row
120 * Returns filename for the image icon, relative to PATH_typo3
121 *
122 * @param string $table The table name
123 * @param array $row The table row ("enablefields" are at least needed for correct icon display and for pages records some more fields in addition!)
124 * @param bool $shaded If set, the icon will be grayed/shaded
125 * @return string Icon filename
126 */
127 static public function getIcon($table, $row = array(), $shaded = FALSE) {
128 // Flags
129 // If set, then the usergroup number will NOT be printed unto the icon. NOTICE.
130 // The icon is generated only if a default icon for groups is not found... So effectively this is ineffective.
131 $doNotRenderUserGroupNumber = TRUE;
132 // Shadow
133 if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && !empty($row['t3ver_state'])) {
134 switch (VersionState::cast($row['t3ver_state'])) {
135 case new VersionState(VersionState::NEW_PLACEHOLDER):
136 return 'gfx/i/shadow_hide.png';
137 break;
138 case new VersionState(VersionState::DELETE_PLACEHOLDER):
139 return 'gfx/i/shadow_delete.png';
140 break;
141 case new VersionState(VersionState::MOVE_PLACEHOLDER):
142 return 'gfx/i/shadow_moveto_plh.png';
143 break;
144 case new VersionState(VersionState::MOVE_POINTER):
145 return 'gfx/i/shadow_moveto_pointer.png';
146 break;
147 }
148 }
149 // First, find the icon file name. This can depend on configuration in TCA, field values and more:
150 if ($table == 'pages') {
151 $iconfile = $GLOBALS['PAGES_TYPES'][$row['doktype']]['icon'];
152 if (!$iconfile) {
153 $iconfile = $GLOBALS['PAGES_TYPES']['default']['icon'];
154 }
155 } else {
156 if (!($iconfile = $GLOBALS['TCA'][$table]['ctrl']['typeicons'][$row[$GLOBALS['TCA'][$table]['ctrl']['typeicon_column']]])) {
157 $iconfile = $GLOBALS['TCA'][$table]['ctrl']['iconfile'] ?: $table . '.gif';
158 }
159 }
160 // Setting path of iconfile if not already set. Default is "gfx/i/"
161 if (!strstr($iconfile, '/')) {
162 $iconfile = 'gfx/i/' . $iconfile;
163 }
164 // Setting the absolute path where the icon should be found as a file:
165 if (substr($iconfile, 0, 3) == '../') {
166 $absfile = PATH_site . substr($iconfile, 3);
167 } else {
168 $absfile = PATH_typo3 . $iconfile;
169 }
170 // Initializing variables, all booleans except otherwise stated:
171 $hidden = FALSE;
172 $timing = FALSE;
173 $futuretiming = FALSE;
174 // In fact an integer value
175 $user = FALSE;
176 $deleted = FALSE;
177 // Set, if a page-record (only pages!) has the extend-to-subpages flag set.
178 $protectSection = FALSE;
179 $noIconFound = $row['_NO_ICON_FOUND'] ? TRUE : FALSE;
180 // + $shaded which is also boolean!
181 // Icon state based on "enableFields":
182 if (is_array($GLOBALS['TCA'][$table]['ctrl']['enablecolumns'])) {
183 $enCols = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
184 // If "hidden" is enabled:
185 if ($enCols['disabled']) {
186 if ($row[$enCols['disabled']]) {
187 $hidden = TRUE;
188 }
189 }
190 // If a "starttime" is set and higher than current time:
191 if ($enCols['starttime']) {
192 if ($GLOBALS['EXEC_TIME'] < (int)$row[$enCols['starttime']]) {
193 $timing = TRUE;
194 // And if "endtime" is NOT set:
195 if ((int)$row[$enCols['endtime']] === 0) {
196 $futuretiming = TRUE;
197 }
198 }
199 }
200 // If an "endtime" is set:
201 if ($enCols['endtime']) {
202 if ((int)$row[$enCols['endtime']] > 0) {
203 if ((int)$row[$enCols['endtime']] < $GLOBALS['EXEC_TIME']) {
204 // End-timing applies at this point.
205 $timing = TRUE;
206 } else {
207 // End-timing WILL apply in the future for this element.
208 $futuretiming = TRUE;
209 }
210 }
211 }
212 // If a user-group field is set:
213 if ($enCols['fe_group']) {
214 $user = $row[$enCols['fe_group']];
215 if ($user && $doNotRenderUserGroupNumber) {
216 $user = 100;
217 }
218 }
219 }
220 // If "deleted" flag is set (only when listing records which are also deleted!)
221 if ($col = $row[$GLOBALS['TCA'][$table]['ctrl']['delete']]) {
222 $deleted = TRUE;
223 }
224 // Detecting extendToSubpages (for pages only)
225 if ($table == 'pages' && $row['extendToSubpages'] && ($hidden || $timing || $futuretiming || $user)) {
226 $protectSection = TRUE;
227 }
228 // If ANY of the booleans are set it means we have to alter the icon:
229 if ($hidden || $timing || $futuretiming || $user || $deleted || $shaded || $noIconFound) {
230 $flags = '';
231 $string = '';
232 if ($deleted) {
233 $string = 'deleted';
234 $flags = 'd';
235 } elseif ($noIconFound) {
236 // This is ONLY for creating icons with "?" on easily...
237 $string = 'no_icon_found';
238 $flags = 'x';
239 } else {
240 if ($hidden) {
241 $string .= 'hidden';
242 }
243 if ($timing) {
244 $string .= 'timing';
245 }
246 if (!$string && $futuretiming) {
247 $string = 'futuretiming';
248 }
249 $flags .= ($hidden ? 'h' : '') . ($timing ? 't' : '') . ($futuretiming ? 'f' : '') . ($user ? 'u' : '') . ($protectSection ? 'p' : '') . ($shaded ? 's' : '');
250 }
251 // Create tagged icon file name:
252 $iconFileName_stateTagged = preg_replace('/.([[:alnum:]]+)$/', '__' . $flags . '.\\1', basename($iconfile));
253 // Check if tagged icon file name exists (a tagged icon means the icon base name with the flags added between body and extension of the filename, prefixed with underscore)
254 if (@is_file((dirname($absfile) . '/' . $iconFileName_stateTagged)) || @is_file(($GLOBALS['TBE_STYLES']['skinImgAutoCfg']['absDir'] . '/' . dirname($iconfile) . '/' . $iconFileName_stateTagged))) {
255 // Look for [iconname]_xxxx.[ext]
256 return dirname($iconfile) . '/' . $iconFileName_stateTagged;
257 } else {
258 // Otherwise, create the icon:
259 $theRes = self::makeIcon($GLOBALS['BACK_PATH'] . $iconfile, $string, $user, $protectSection, $absfile, $iconFileName_stateTagged);
260 return $theRes;
261 }
262 } else {
263 return $iconfile;
264 }
265 }
266
267 /**
268 * Returns the src=... for the input $src value OR any alternative found in $TBE_STYLES['skinImg']
269 * Used for skinning the TYPO3 backend with an alternative set of icons
270 *
271 * @param string $backPath Current backpath to PATH_typo3 folder
272 * @param string $src Icon file name relative to PATH_typo3 folder
273 * @param string $wHattribs Default width/height, defined like 'width="12" height="14"'
274 * @param int $outputMode Mode: 0 (zero) is default and returns src/width/height. 1 returns value of src+backpath, 2 returns value of w/h.
275 * @return string Returns ' src="[backPath][src]" [wHattribs]'
276 * @see skinImgFile()
277 */
278 static public function skinImg($backPath, $src, $wHattribs = '', $outputMode = 0) {
279 static $cachedSkinImages = array();
280 $imageId = md5($backPath . $src . $wHattribs . $outputMode);
281 if (isset($cachedSkinImages[$imageId])) {
282 return $cachedSkinImages[$imageId];
283 }
284 // Setting source key. If the icon is referred to inside an extension, we homogenize the prefix to "ext/":
285 $srcKey = preg_replace('/^(\\.\\.\\/typo3conf\\/ext|sysext|ext)\\//', 'ext/', $src);
286 // LOOKING for alternative icons:
287 if ($GLOBALS['TBE_STYLES']['skinImg'][$srcKey]) {
288 // Slower or faster with is_array()? Could be used.
289 list($src, $wHattribs) = $GLOBALS['TBE_STYLES']['skinImg'][$srcKey];
290 } elseif ($GLOBALS['TBE_STYLES']['skinImgAutoCfg']) {
291 // Otherwise, test if auto-detection is enabled:
292 // Search for alternative icon automatically:
293 $fExt = $GLOBALS['TBE_STYLES']['skinImgAutoCfg']['forceFileExtension'];
294 $scaleFactor = $GLOBALS['TBE_STYLES']['skinImgAutoCfg']['scaleFactor'] ?: 1;
295 // Scaling factor
296 $lookUpName = $fExt ? preg_replace('/\\.[[:alnum:]]+$/', '', $srcKey) . '.' . $fExt : $srcKey;
297 // Set filename to look for
298 if ($fExt && !@is_file(($GLOBALS['TBE_STYLES']['skinImgAutoCfg']['absDir'] . $lookUpName))) {
299 // Fallback to original filename if icon with forced extension doesn't exists
300 $lookUpName = $srcKey;
301 }
302 // If file is found:
303 if (@is_file(($GLOBALS['TBE_STYLES']['skinImgAutoCfg']['absDir'] . $lookUpName))) {
304 // If there is a file...
305 $iInfo = @getimagesize(($GLOBALS['TBE_STYLES']['skinImgAutoCfg']['absDir'] . $lookUpName));
306 // Get width/height:
307 // Set $src and $wHattribs:
308 $src = $GLOBALS['TBE_STYLES']['skinImgAutoCfg']['relDir'] . $lookUpName;
309 $wHattribs = 'width="' . round($iInfo[0] * $scaleFactor) . '" height="' . round($iInfo[1] * $scaleFactor) . '"';
310 }
311 // In any case, set currect src / wHattrib - this way we make sure that an entry IS found next time we hit the function,
312 // regardless of whether it points to a alternative icon or just the current.
313 $GLOBALS['TBE_STYLES']['skinImg'][$srcKey] = array($src, $wHattribs);
314 }
315 // Rendering disabled (greyed) icons using _i (inactive) as name suffix ("_d" is already used)
316 $matches = array();
317 $srcBasename = basename($src);
318 if (preg_match('/(.*)_i(\\....)$/', $srcBasename, $matches)) {
319 $temp_path = dirname(PATH_thisScript) . '/';
320 if (!@is_file(($temp_path . $backPath . $src))) {
321 $srcOrg = preg_replace('/_i' . preg_quote($matches[2], '/') . '$/', $matches[2], $src);
322 $src = self::makeIcon($backPath . $srcOrg, 'disabled', 0, FALSE, $temp_path . $backPath . $srcOrg, $srcBasename);
323 }
324 }
325 // Return icon source/wHattributes:
326 $output = '';
327 switch ($outputMode) {
328 case 0:
329 $output = ' src="' . $backPath . $src . '" ' . $wHattribs;
330 break;
331 case 1:
332 $output = $backPath . $src;
333 break;
334 case 2:
335 $output = $wHattribs;
336 break;
337 }
338 $cachedSkinImages[$imageId] = $output;
339 return $output;
340 }
341
342 /***********************************
343 *
344 * Other functions
345 *
346 ***********************************/
347 /**
348 * Creates the icon file for the function getIcon()
349 *
350 * @param string $iconfile Original unprocessed Icon file, relative path to PATH_typo3
351 * @param string $mode Mode string, eg. "deleted" or "futuretiming" determining how the icon will look
352 * @param int $user The number of the fe_group record uid if applicable
353 * @param bool $protectSection Flag determines if the protected-section icon should be applied.
354 * @param string $absFile Absolute path to file from which to create the icon.
355 * @param string $iconFileName_stateTagged The filename that this icon should have had, basically [icon base name]_[flags].[extension] - used for part of temporary filename
356 * @return string Filename relative to PATH_typo3
357 * @access private
358 */
359 static public function makeIcon($iconfile, $mode, $user, $protectSection, $absFile, $iconFileName_stateTagged) {
360 $iconFileName = 'icon_' . GeneralUtility::shortMD5(($iconfile . '|' . $mode . '|-' . $user . '|' . $protectSection)) . '_' . $iconFileName_stateTagged . '.' . ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png'] ? 'png' : 'gif');
361 $mainpath = '../typo3temp/' . $iconFileName;
362 $path = PATH_site . 'typo3temp/' . $iconFileName;
363 if (file_exists(PATH_typo3 . 'icons/' . $iconFileName)) {
364 // Returns if found in typo3/icons/
365 return 'icons/' . $iconFileName;
366 } elseif (file_exists($path)) {
367 // Returns if found in ../typo3temp/icons/
368 return $mainpath;
369 } else {
370 // Makes icon:
371 if (file_exists($absFile)) {
372 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
373 // Create image pointer, if possible
374 $im = self::imagecreatefrom($absFile);
375 if ($im < 0) {
376 return $iconfile;
377 }
378 // Converting to gray scale, dimming the icon:
379 if ($mode == 'disabled' or $mode != 'futuretiming' && $mode != 'no_icon_found' && !(!$mode && $user)) {
380 $totalImageColors = ImageColorsTotal($im);
381 for ($c = 0; $c < $totalImageColors; $c++) {
382 $cols = ImageColorsForIndex($im, $c);
383 $newcol = round(($cols['red'] + $cols['green'] + $cols['blue']) / 3);
384 $lighten = $mode == 'disabled' ? 2.5 : 2;
385 $newcol = round(255 - (255 - $newcol) / $lighten);
386 ImageColorSet($im, $c, $newcol, $newcol, $newcol);
387 }
388 }
389 // Applying user icon, if there are access control on the item:
390 if ($user) {
391 if ($user < 100) {
392 // Apply user number only if lower than 100
393 $black = ImageColorAllocate($im, 0, 0, 0);
394 imagefilledrectangle($im, 0, 0, $user > 10 ? 9 : 5, 8, $black);
395 $white = ImageColorAllocate($im, 255, 255, 255);
396 imagestring($im, 1, 1, 1, $user, $white);
397 }
398 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_group.gif');
399 if ($ol_im < 0) {
400 return $iconfile;
401 }
402 self::imagecopyresized($im, $ol_im, 0, 0, 0, 0, imagesx($ol_im), imagesy($ol_im), imagesx($ol_im), imagesy($ol_im));
403 }
404 // Applying overlay based on mode:
405 if ($mode) {
406 unset($ol_im);
407 switch ($mode) {
408 case 'deleted':
409 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_deleted.gif');
410 break;
411 case 'futuretiming':
412 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_timing.gif');
413 break;
414 case 'timing':
415 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_timing.gif');
416 break;
417 case 'hiddentiming':
418 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_hidden_timing.gif');
419 break;
420 case 'no_icon_found':
421 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_no_icon_found.gif');
422 break;
423 case 'disabled':
424 // is already greyed - nothing more
425 $ol_im = 0;
426 break;
427 case 'hidden':
428
429 default:
430 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_hidden.gif');
431 }
432 if ($ol_im < 0) {
433 return $iconfile;
434 }
435 if ($ol_im) {
436 self::imagecopyresized($im, $ol_im, 0, 0, 0, 0, imagesx($ol_im), imagesy($ol_im), imagesx($ol_im), imagesy($ol_im));
437 }
438 }
439 // Protect-section icon:
440 if ($protectSection) {
441 $ol_im = self::imagecreatefrom($GLOBALS['BACK_PATH'] . 'gfx/overlay_sub5.gif');
442 if ($ol_im < 0) {
443 return $iconfile;
444 }
445 self::imagecopyresized($im, $ol_im, 0, 0, 0, 0, imagesx($ol_im), imagesy($ol_im), imagesx($ol_im), imagesy($ol_im));
446 }
447 // Create the image as file, destroy GD image and return:
448 @self::imagemake($im, $path);
449 GeneralUtility::gif_compress($path, 'IM');
450 ImageDestroy($im);
451 return $mainpath;
452 } else {
453 return $iconfile;
454 }
455 } else {
456 return $GLOBALS['BACK_PATH'] . 'gfx/fileicons/default.gif';
457 }
458 }
459 }
460
461 /**
462 * The necessity of using this function for combining two images if GD is version 2 is that
463 * GD2 cannot manage to combine two indexed-color images without totally spoiling everything.
464 * In class \TYPO3\CMS\Core\Imaging\GraphicalFunctions this was solved by combining the images
465 * onto a first created true color image.
466 * However it has turned out that this method will not work if the indexed png-files contains transparency.
467 * So I had to turn my attention to ImageMagick - my 'enemy of death'.
468 * And so it happend - ImageMagick is now used to combine my two indexed-color images with transparency. And that works.
469 * Of course it works only if ImageMagick is able to create valid png-images - which you cannot be sure of with older versions (still 5+)
470 * The only drawback is (apparently) that IM creates true-color png's. The transparency of these will not be shown by MSIE on windows at this time (although it's straight 0%/100% transparency!) and the file size may be larger.
471 *
472 * @param resource $destinationImage Destination image
473 * @param resource $sourceImage Source image
474 * @param int $destinationX Destination x-coordinate
475 * @param int $destinationY Destination y-coordinate
476 * @param int $sourceX Source x-coordinate
477 * @param int $sourceY Source y-coordinate
478 * @param int $destinationWidth Destination width
479 * @param int $destinationHeight Destination height
480 * @param int $sourceWidth Source width
481 * @param int $sourceHeight Source height
482 * @return void
483 * @access private
484 * @see \TYPO3\CMS\Core\Imaging\GraphicalFunctions::imagecopyresized()
485 */
486 static public function imagecopyresized(&$destinationImage, $sourceImage, $destinationX, $destinationY, $sourceX, $sourceY, $destinationWidth, $destinationHeight, $sourceWidth, $sourceHeight) {
487 imagecopyresized($destinationImage, $sourceImage, $destinationX, $destinationY, $sourceX, $sourceY, $destinationWidth, $destinationHeight, $sourceWidth, $sourceHeight);
488 }
489
490 /**
491 * Create new image pointer from input file (either gif/png, in case the wrong format it is converted by \TYPO3\CMS\Core\Utility\GeneralUtility::read_png_gif())
492 *
493 * @param string $file Absolute filename of the image file from which to start the icon creation.
494 * @return mixed If success, image pointer, otherwise "-1
495 * @access private
496 * @see \TYPO3\CMS\Core\Utility\GeneralUtility::read_png_gif
497 */
498 static public function imagecreatefrom($file) {
499 $file = GeneralUtility::read_png_gif($file, $GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']);
500 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
501 return $file ? imagecreatefrompng($file) : -1;
502 } else {
503 return $file ? imagecreatefromgif($file) : -1;
504 }
505 }
506
507 /**
508 * Write the icon in $im pointer to $path
509 *
510 * @param pointer $im Pointer to GDlib image resource
511 * @param string $path Absolute path to the filename in which to write the icon.
512 * @return void
513 * @access private
514 */
515 static public function imagemake($im, $path) {
516 if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib_png']) {
517 @ImagePng($im, $path);
518 } else {
519 @ImageGif($im, $path);
520 }
521 if (@is_file($path)) {
522 GeneralUtility::fixPermissions($path);
523 }
524 }
525
526 /**********************************************
527 * SPRITE ICON API
528 *
529 * The Sprite Icon API helps you to quickly get the HTML for any icon you want
530 * this is typically wrapped in a <span> tag with corresponding CSS classes that
531 * will be responsible for the
532 *
533 * There are four ways to use this API:
534 *
535 * 1) for any given TCA record
536 * $spriteIconHtml = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForRecord('pages', $row);
537 *
538 * 2) for any given File of Folder object
539 * $spriteIconHtml = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForResource($fileOrFolderObject);
540 *
541 * 3) for any given file
542 * $spriteIconHtml = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconForFile('myimage.png');
543 *
544 * 4) for any other icon you know the name
545 * $spriteIconHtml = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIcon('actions-document-open');
546 *
547 **********************************************/
548 /**
549 * This generic method is used throughout the TYPO3 Backend to show icons
550 * in any variation which are not bound to any resource object (see getSpriteIconForResource)
551 * or database record (see getSpriteIconForRecord)
552 *
553 * Generates a HTML tag with proper CSS classes. The TYPO3 skin has
554 * defined these CSS classes already to have a pre-defined background image,
555 * and the correct background-position to show the necessary icon.
556 *
557 * If no options or overlays are given, the icon will be cached in
558 * $spriteIconCache.
559 *
560 * @param string $iconName The name of the icon to fetch
561 * @param array $options An associative array with additional options and attributes for the tag. by default, the key is the name of the attribute, and the value is the parameter string that is set. However, there are some additional special reserved keywords that can be used as keys: "html" (which is the HTML that will be inside the icon HTML tag), "tagName" (which is an alternative tagName than "span"), and "class" (additional class names that will be merged with the sprite icon CSS classes)
562 * @param array $overlays An associative array with the icon-name as key, and the options for this overlay as an array again (see the parameter $options again)
563 *
564 * @return string The full HTML tag (usually a <span>)
565 * @access public
566 */
567 static public function getSpriteIcon($iconName, array $options = array(), array $overlays = array()) {
568 // Check if icon can be cached and return cached version if present
569 if (empty($options) && empty($overlays)) {
570 if (isset(static::$spriteIconCache[$iconName])) {
571 return static::$spriteIconCache[$iconName];
572 }
573 $iconIsCacheable = TRUE;
574 } else {
575 $iconIsCacheable = FALSE;
576 }
577
578 $innerHtml = isset($options['html']) ? $options['html'] : NULL;
579 $tagName = isset($options['tagName']) ? $options['tagName'] : NULL;
580
581 // Deal with the overlays
582 foreach ($overlays as $overlayIconName => $overlayOptions) {
583 $overlayOptions['html'] = $innerHtml;
584 $overlayOptions['class'] = (isset($overlayOptions['class']) ? $overlayOptions['class'] . ' ' : '') . 't3-icon-overlay';
585 $innerHtml = self::getSpriteIcon($overlayIconName, $overlayOptions);
586 }
587
588 $availableIcons = isset($GLOBALS['TBE_STYLES']['spriteIconApi']['iconsAvailable'])
589 ? (array)$GLOBALS['TBE_STYLES']['spriteIconApi']['iconsAvailable']
590 : array();
591 if ($iconName !== 'empty-empty' && !in_array($iconName, $availableIcons, TRUE)) {
592 $iconName = 'status-status-icon-missing';
593 }
594
595 // Create the CSS class
596 $options['class'] = self::getSpriteIconClasses($iconName) . (isset($options['class']) ? ' ' . $options['class'] : '');
597 unset($options['html'], $options['tagName']);
598 $spriteHtml = self::buildSpriteHtmlIconTag($options, $innerHtml, $tagName);
599
600 // Store result in cache if possible
601 if ($iconIsCacheable) {
602 static::$spriteIconCache[$iconName] = $spriteHtml;
603 }
604 return $spriteHtml;
605 }
606
607 /**
608 * This method is used throughout the TYPO3 Backend to show icons for a file type
609 *
610 * Generates a HTML tag with proper CSS classes. The TYPO3 skin has defined these CSS classes
611 * already to have a pre-defined background image, and the correct background-position to show
612 * the necessary icon.
613 *
614 * @param string $fileExtension The name of the icon to fetch, can be a file extension, full file path or one of the special keywords "folder" or "mount
615 * @param array $options An associative array with additional options and attributes for the tag. by default, the key is the name of the attribute, and the value is the parameter string that is set. However, there are some additional special reserved keywords that can be used as keys: "html" (which is the HTML that will be inside the icon HTML tag), "tagName" (which is an alternative tagName than "span"), and "class" (additional class names that will be merged with the sprite icon CSS classes)
616 * @return string The full HTML tag (usually a <span>)
617 * @access public
618 */
619 static public function getSpriteIconForFile($fileExtension, array $options = array()) {
620 $innerHtml = isset($options['html']) ? $options['html'] : NULL;
621 $tagName = isset($options['tagName']) ? $options['tagName'] : NULL;
622 // Create the CSS class
623 $options['class'] = self::mapFileExtensionToSpriteIconClass($fileExtension) . (isset($options['class']) ? ' ' . $options['class'] : '');
624 unset($options['html']);
625 unset($options['tagName']);
626 return self::buildSpriteHtmlIconTag($options, $innerHtml, $tagName);
627 }
628
629 /**
630 * Generates the spriteicon css classes name for a given path or fileExtension
631 * usually called from getSpriteIconForFile or ExtJs Provider
632 *
633 * @param string $fileExtension FileExtension can be jpg, gif etc, but also be 'mount' or 'folder', but can also be a full path which will be resolved then
634 * @return string The string of the CSS class, see \TYPO3\CMS\Backend\Utility\IconUtility::$fileSpriteIconNames
635 * @access private
636 */
637 static public function mapFileExtensionToSpriteIconClass($fileExtension) {
638 return self::getSpriteIconClasses(self::mapFileExtensionToSpriteIconName($fileExtension));
639 }
640
641 /**
642 * Generates the spriteicon name for a given path or fileExtension
643 * usually called from mapFileExtensionToSpriteIconClass and tceforms
644 *
645 * @param string $fileExtension FileExtension can be jpg, gif etc, but also be 'mount' or 'folder', but can also be a full path which will be resolved then
646 * @return string The string of the CSS class, see \TYPO3\CMS\Backend\Utility\IconUtility::$fileSpriteIconNames
647 * @access private
648 */
649 static public function mapFileExtensionToSpriteIconName($fileExtension) {
650 // If the file is a whole file with name etc (mainly, if it has a "." or a "/"),
651 // then it is checked whether it is a valid directory
652 if (strpos($fileExtension, '.') !== FALSE || strpos($fileExtension, '/') !== FALSE) {
653 // Check if it is a directory
654 $filePath = dirname(GeneralUtility::getIndpEnv('SCRIPT_FILENAME')) . '/' . $GLOBALS['BACK_PATH'] . $fileExtension;
655 $path = GeneralUtility::resolveBackPath($filePath);
656 if (is_dir($path) || substr($fileExtension, -1) === '/' || substr($fileExtension, -1) === '\\') {
657 $fileExtension = 'folder';
658 } else {
659 if (($pos = strrpos($fileExtension, '.')) !== FALSE) {
660 $fileExtension = strtolower(substr($fileExtension, $pos + 1));
661 } else {
662 $fileExtension = 'default';
663 }
664 }
665 }
666 // If the file extension is not valid
667 // then use the default one
668 if (!isset(self::$fileSpriteIconNames[$fileExtension])) {
669 $fileExtension = 'default';
670 }
671 $iconName = self::$fileSpriteIconNames[$fileExtension];
672 return $iconName;
673 }
674
675 /**
676 * This method is used throughout the TYPO3 Backend to show icons for a DB record
677 *
678 * Generates a HTML tag with proper CSS classes. The TYPO3 skin has defined these CSS classes
679 * already to have a pre-defined background image, and the correct background-position to show
680 * the necessary icon.
681 *
682 * @param string $table The TCA table name
683 * @param array $row The DB record of the TCA table
684 * @param array $options An associative array with additional options and attributes for the tag. by default, the key is the name of the attribute, and the value is the parameter string that is set. However, there are some additional special reserved keywords that can be used as keys: "html" (which is the HTML that will be inside the icon HTML tag), "tagName" (which is an alternative tagName than "span"), and "class" (additional class names that will be merged with the sprite icon CSS classes)
685 * @return string The full HTML tag (usually a <span>)
686 * @access public
687 */
688 static public function getSpriteIconForRecord($table, array $row, array $options = array()) {
689 $innerHtml = isset($options['html']) ? $options['html'] : NULL;
690 $tagName = isset($options['tagName']) ? $options['tagName'] : NULL;
691 // Overlay this record icon with the status of the row
692 $overlaySpriteIconName = self::mapRecordOverlayToSpriteIconName($table, $row);
693 if ($overlaySpriteIconName) {
694 $overlayOptions = array(
695 'html' => $innerHtml,
696 'class' => 't3-icon-overlay'
697 );
698 $innerHtml = self::getSpriteIcon($overlaySpriteIconName, $overlayOptions);
699 }
700 // Fetch the name for the CSS class, based on the $row
701 $options['class'] = self::mapRecordTypeToSpriteIconClass($table, $row) . (isset($options['class']) ? ' ' . $options['class'] : '');
702 unset($options['html']);
703 unset($options['tagName']);
704 return self::buildSpriteHtmlIconTag($options, $innerHtml, $tagName);
705 }
706
707 /**
708 * This method is used throughout the TYPO3 Backend to show icons for files and folders
709 *
710 * The method takes care of the translation of file extension to proper icon and for folders
711 * it will return the icon depending on the role of the folder.
712 *
713 * If the given resource is a folder there are some additional options that can be used:
714 * - mount-root => TRUE (to indicate this is the root of a mount)
715 * - folder-open => TRUE (to indicate that the folder is opened in the file tree)
716 *
717 * There is a hook in place to manipulate the icon name and overlays.
718 *
719 * @param \TYPO3\CMS\Core\Resource\ResourceInterface $resource
720 * @param array $options An associative array with additional options and attributes for the tag. See self::getSpriteIcon()
721 * @param array $overlays An associative array with the icon-name as key, and the options for this overlay as an array again (see the parameter $options again)
722 * @return string
723 * @throws \UnexpectedValueException
724 */
725 static public function getSpriteIconForResource(\TYPO3\CMS\Core\Resource\ResourceInterface $resource, array $options = array(), array $overlays = array()) {
726 // Folder
727 if ($resource instanceof \TYPO3\CMS\Core\Resource\FolderInterface) {
728 $iconName = NULL;
729 $role = $resource->getRole();
730 // non browsable storage
731 if ($resource->getStorage()->isBrowsable() === FALSE && !empty($options['mount-root'])) {
732 $iconName = 'apps-filetree-folder-locked';
733 } else {
734 // storage root
735 if ($resource->getStorage()->getRootLevelFolder()->getIdentifier() === $resource->getIdentifier()) {
736 $iconName = 'apps-filetree-root';
737 }
738
739
740 // user/group mount root
741 if (!empty($options['mount-root'])) {
742 $iconName = 'apps-filetree-mount';
743 if ($role === \TYPO3\CMS\Core\Resource\FolderInterface::ROLE_READONLY_MOUNT) {
744 $overlays['status-overlay-locked'] = array();
745 } elseif ($role === \TYPO3\CMS\Core\Resource\FolderInterface::ROLE_USER_MOUNT) {
746 $overlays['status-overlay-access-restricted'] = array();
747 }
748 }
749
750 if ($iconName === NULL) {
751 // in folder tree view $options['folder-open'] can define a open folder icon
752 if (!empty($options['folder-open'])) {
753 $iconName = 'apps-filetree-folder-opened';
754 } else {
755 $iconName = 'apps-filetree-folder-default';
756 }
757
758 if ($role === \TYPO3\CMS\Core\Resource\FolderInterface::ROLE_TEMPORARY) {
759 $iconName = 'apps-filetree-folder-temp';
760 } elseif ($role === \TYPO3\CMS\Core\Resource\FolderInterface::ROLE_RECYCLER) {
761 $iconName = 'apps-filetree-folder-recycler';
762 }
763 }
764
765 // if locked add overlay
766 if ($resource instanceof \TYPO3\CMS\Core\Resource\InaccessibleFolder ||
767 !$resource->getStorage()->checkFolderActionPermission('add', $resource)
768 ) {
769 $overlays['status-overlay-locked'] = array();
770 }
771 }
772
773
774
775 // File
776 } else {
777 $iconName = self::mapFileExtensionToSpriteIconName($resource->getExtension());
778
779 if ($resource instanceof \TYPO3\CMS\Core\Resource\File && $resource->isMissing()) {
780 $overlays['status-overlay-missing'] = array();
781 }
782 }
783
784 // Hook: allow some other process to influence the choice of icon and overlays
785 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['overrideResourceIcon'])) {
786 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['overrideResourceIcon'] as $classRef) {
787 $hookObject = GeneralUtility::getUserObj($classRef);
788 if (!$hookObject instanceof IconUtilityOverrideResourceIconHookInterface) {
789 throw new \UnexpectedValueException('$hookObject must implement interface ' . \TYPO3\CMS\Backend\Utility\IconUtilityOverrideResourceIconHookInterface::class, 1393574895);
790 }
791 $hookObject->overrideResourceIcon($resource, $iconName, $options, $overlays);
792 }
793 }
794
795 unset($options['mount-root']);
796 unset($options['folder-open']);
797 return self::getSpriteIcon($iconName, $options, $overlays);
798 }
799
800 /**
801 * this helper functions looks up the column that is used for the type of
802 * the chosen TCA table. And then fetches the corresponding class
803 * based on the chosen iconsprite class in this TCA
804 * The TCA looks up
805 * - [ctrl][typeicon_column]
806 * -
807 * This method solely takes care of the type of this record, not any
808 * statuses, used for overlays.
809 * You should not use this directly besides if you need classes for ExtJS iconCls.
810 *
811 * see ext:core/Configuration/TCA/pages.php for an example with the TCA table "pages"
812 *
813 * @param string $table The TCA table
814 * @param array $row The selected record
815 * @return string The CSS class for the sprite icon of that DB record
816 * @access private
817 */
818 static public function mapRecordTypeToSpriteIconClass($table, array $row) {
819 return self::getSpriteIconClasses(self::mapRecordTypeToSpriteIconName($table, $row));
820 }
821
822 /**
823 * this helper functions looks up the column that is used for the type of
824 * the chosen TCA table. And then fetches the corresponding iconname
825 * based on the chosen iconsprite class in this TCA
826 * The TCA looks up
827 * - [ctrl][typeicon_column]
828 * -
829 * This method solely takes care of the type of this record, not any
830 * statuses, used for overlays.
831 * You should not use this directly besides if you need it in tceforms/core classes
832 *
833 * see ext:core/Configuration/TCA/pages.php for an example with the TCA table "pages"
834 *
835 * @param string $tableThe TCA table
836 * @param array $row The selected record
837 * @return string The CSS class for the sprite icon of that DB record
838 * @access private
839 */
840 static public function mapRecordTypeToSpriteIconName($table, array $row) {
841 $recordType = array();
842 $ref = NULL;
843 if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_column'])) {
844 $column = $GLOBALS['TCA'][$table]['ctrl']['typeicon_column'];
845 if (isset($row[$column])) {
846 $recordType[1] = $row[$column];
847 } else {
848 $recordType[1] = 'default';
849 }
850 // Workaround to give nav_hide pages a complete different icon
851 // Although it's not a separate doctype
852 // and to give root-pages an own icon
853 if ($table === 'pages') {
854 if ($row['nav_hide']) {
855 $recordType[2] = $recordType[1] . '-hideinmenu';
856 }
857 if ($row['is_siteroot']) {
858 $recordType[3] = $recordType[1] . '-root';
859 }
860 if ($row['module']) {
861 $recordType[4] = 'contains-' . $row['module'];
862 }
863 }
864 if (is_array($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])) {
865 foreach ($recordType as $key => $type) {
866 if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'][$type])) {
867 $recordType[$key] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'][$type];
868 } else {
869 unset($recordType[$key]);
870 }
871 }
872 $recordType[0] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['default'];
873 if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['mask'])) {
874 $recordType[5] = str_replace('###TYPE###', $row[$column], $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['mask']);
875 }
876 if (isset($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['userFunc'])) {
877 $parameters = array('row' => $row);
878 $recordType[6] = GeneralUtility::callUserFunction($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['userFunc'], $parameters, $ref);
879 }
880 } else {
881 foreach ($recordType as &$type) {
882 $type = 'tcarecords-' . $table . '-' . $type;
883 }
884 unset($type);
885 $recordType[0] = 'tcarecords-' . $table . '-default';
886 }
887 } else {
888 if (is_array($GLOBALS['TCA'][$table]['ctrl']['typeicon_classes'])) {
889 $recordType[0] = $GLOBALS['TCA'][$table]['ctrl']['typeicon_classes']['default'];
890 } else {
891 $recordType[0] = 'tcarecords-' . $table . '-default';
892 }
893 }
894 krsort($recordType);
895 if (is_array($GLOBALS['TBE_STYLES']['spriteIconApi']['iconsAvailable'])) {
896 foreach ($recordType as $iconName) {
897 if (in_array($iconName, $GLOBALS['TBE_STYLES']['spriteIconApi']['iconsAvailable'])) {
898 return $iconName;
899 }
900 }
901 }
902 return 'status-status-icon-missing';
903 }
904
905 /**
906 * this helper functions checks if the DB record ($row) has any special status
907 * based on the TCA settings like hidden, starttime etc, and then returns a specific
908 * Sprite icon class for the overlay of this DB record
909 * This method solely takes care of the overlay of this record, not any type
910 *
911 * Please note that this only returns one overlay, one status, that is prioritized
912 * by $GLOBALS['TYPO3_CONF_VARS']['BE']['spriteIconRecordOverlayPriorities']
913 * We wanted to not have these icons blown over by tons of overlays, so this is limited
914 * to just one.
915 *
916 * see ext:core/Configuration/DefaultConfiguration.php for the default options, you will find
917 * $GLOBALS['TYPO3_CONF_VARS']['BE']['spriteIconRecordOverlayNames'] that shows
918 * the list of CSS classes that will be used for the sprites, mapped to the statuses here
919 *
920 * @param string $table The TCA table
921 * @param array $row The selected record
922 * @return string The CSS class for the sprite icon of that DB record
923 * @access private
924 */
925 static public function mapRecordOverlayToSpriteIconName($table, array $row) {
926 $tcaCtrl = $GLOBALS['TCA'][$table]['ctrl'];
927 // Calculate for a given record the actual visibility at the moment
928 $status = array(
929 'hidden' => FALSE,
930 'starttime' => FALSE,
931 'endtime' => FALSE,
932 'futureendtime' => FALSE,
933 'fe_group' => FALSE,
934 'deleted' => FALSE,
935 'protectedSection' => FALSE,
936 'nav_hide' => $row['nav_hide'] ? TRUE : FALSE,
937 'noIconFound' => $row['_NO_ICON_FOUND'] ? TRUE : FALSE
938 );
939 // Icon state based on "enableFields":
940 if (is_array($tcaCtrl['enablecolumns'])) {
941 $enCols = $tcaCtrl['enablecolumns'];
942 // If "hidden" is enabled:
943 if ($tcaCtrl['enablecolumns']['disabled'] && $row[$tcaCtrl['enablecolumns']['disabled']]) {
944 $status['hidden'] = TRUE;
945 }
946 // If a "starttime" is set and higher than current time:
947 if ($tcaCtrl['enablecolumns']['starttime'] && $GLOBALS['EXEC_TIME'] < (int)$row[$tcaCtrl['enablecolumns']['starttime']]) {
948 $status['starttime'] = TRUE;
949 }
950 // If an "endtime" is set
951 if ($tcaCtrl['enablecolumns']['endtime']) {
952 if ((int)$row[$tcaCtrl['enablecolumns']['endtime']] > 0) {
953 if ((int)$row[$tcaCtrl['enablecolumns']['endtime']] < $GLOBALS['EXEC_TIME']) {
954 // End-timing applies at this point.
955 $status['endtime'] = TRUE;
956 } else {
957 // End-timing WILL apply in the future for this element.
958 $status['futureendtime'] = TRUE;
959 }
960 }
961 }
962 // If a user-group field is set
963 if ($tcaCtrl['enablecolumns']['fe_group'] && $row[$tcaCtrl['enablecolumns']['fe_group']]) {
964 $status['fe_group'] = TRUE;
965 }
966 }
967 // If "deleted" flag is set (only when listing records which are also deleted!)
968 if ($row[$tcaCtrl['delete']]) {
969 $status['deleted'] = TRUE;
970 }
971 // Detecting extendToSubpages (for pages only)
972 if ($table == 'pages' && $row['extendToSubpages']) {
973 $status['protectedSection'] = TRUE;
974 }
975 // Hook: allow some other process to influence the choice of icon overlay
976 // The method called receives the table name, the current row and the current status array as parameters
977 // The status array should be passed as a reference and in order to be modified within the hook
978 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['overrideIconOverlay'])) {
979 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_iconworks.php']['overrideIconOverlay'] as $classRef) {
980 $hookObject = GeneralUtility::getUserObj($classRef);
981 if (method_exists($hookObject, 'overrideIconOverlay')) {
982 $hookObject->overrideIconOverlay($table, $row, $status);
983 }
984 }
985 }
986 // Now only show the status with the highest priority
987 $priorities = $GLOBALS['TBE_STYLES']['spriteIconApi']['spriteIconRecordOverlayPriorities'];
988 $iconName = '';
989 if (is_array($priorities)) {
990 foreach ($priorities as $priority) {
991 if ($status[$priority]) {
992 $iconName = $GLOBALS['TBE_STYLES']['spriteIconApi']['spriteIconRecordOverlayNames'][$priority];
993 break;
994 }
995 }
996 }
997 return $iconName;
998 }
999
1000 /**
1001 * generic method to create the final CSS classes based on the sprite icon name
1002 * with the base class and splits the name into parts
1003 * is usually called by the methods that are responsible for fetching the names
1004 * out of the file name, or the record type
1005 *
1006 * @param string $iconName Iconname like 'actions-document-new'
1007 * @return string A list of all CSS classes needed for the HTML tag
1008 */
1009 static public function getSpriteIconClasses($iconName) {
1010 $cssClasses = ($baseCssClass = 't3-icon');
1011 $parts = explode('-', $iconName);
1012 if (count($parts) > 1) {
1013 // Will be something like "t3-icon-actions"
1014 $cssClasses .= ' ' . ($baseCssClass . '-' . $parts[0]);
1015 // Will be something like "t3-icon-actions-document"
1016 $cssClasses .= ' ' . ($baseCssClass . '-' . $parts[0] . '-' . $parts[1]);
1017 // Will be something like "t3-icon-document-new"
1018 $cssClasses .= ' ' . ($baseCssClass . '-' . substr($iconName, (strlen($parts[0]) + 1)));
1019 }
1020 static::emitBuildSpriteIconClassesSignal($iconName, $cssClasses);
1021 return $cssClasses;
1022 }
1023
1024 /**
1025 * low level function that generates the HTML tag for the sprite icon
1026 * is usually called by the three API classes (getSpriteIcon, getSpriteIconForFile, getSpriteIconForRecord)
1027 * it does not care about classes or anything else, but just plainly builds the HTML tag
1028 *
1029 * @param array $tagAttributes An associative array of additional tagAttributes for the HTML tag
1030 * @param string $innerHtml The content within the tag, a "&nbsp;" by default
1031 * @param string $tagName The name of the HTML element that should be used (span by default)
1032 * @return string The sprite html icon tag
1033 */
1034 static protected function buildSpriteHtmlIconTag(array $tagAttributes, $innerHtml = NULL, $tagName = NULL) {
1035 list($tagAttributes, $innerHtml, $tagName) = static::emitBuildSpriteHtmlIconTagSignal($tagAttributes, $innerHtml, $tagName);
1036
1037 $innerHtml = $innerHtml === NULL ? ' ' : $innerHtml;
1038 $tagName = $tagName === NULL ? 'span' : $tagName;
1039 $attributes = '';
1040 foreach ($tagAttributes as $attribute => $value) {
1041 $attributes .= ' ' . htmlspecialchars($attribute) . '="' . htmlspecialchars($value) . '"';
1042 }
1043
1044 return '<' . $tagName . $attributes . '>' . $innerHtml . '</' . $tagName . '>';
1045 }
1046
1047 /**
1048 * @param array $tagAttributes An associative array of additional tagAttributes for the HTML tag
1049 * @param string $innerHtml The content within the tag, NULL by default
1050 * @param string $tagName The name of the HTML element that should be used (span by default), NULL by default
1051 * @return array
1052 */
1053 static protected function emitBuildSpriteHtmlIconTagSignal(array $tagAttributes, $innerHtml, $tagName) {
1054 return static::getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Backend\Utility\IconUtility::class, 'buildSpriteHtmlIconTag', array($tagAttributes, $innerHtml, $tagName));
1055 }
1056
1057 /**
1058 * Emits a signal right after the CSS classes are built. This is useful if somebody only
1059 * fetches the CSS classes via IconUtility and not the whole sprite span tag.
1060 *
1061 * @param string $iconName The name of the icon
1062 * @param string $cssClasses the CSS classes to be used as a string
1063 */
1064 static protected function emitBuildSpriteIconClassesSignal($iconName, &$cssClasses) {
1065 static::getSignalSlotDispatcher()->dispatch(\TYPO3\CMS\Backend\Utility\IconUtility::class, 'buildSpriteIconClasses', array($iconName, &$cssClasses));
1066 }
1067
1068 /**
1069 * Get the SignalSlot dispatcher
1070 *
1071 * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
1072 */
1073 static protected function getSignalSlotDispatcher() {
1074 return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class);
1075 }
1076
1077 }