TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,576 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Imaging;
|
||||
|
||||
use TYPO3\CMS\Core\Cache\Event\CacheWarmupEvent;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\Exception;
|
||||
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
|
||||
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
|
||||
use TYPO3\CMS\Core\Imaging\IconProvider\SvgSpriteIconProvider;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class which makes it possible to register custom icons
|
||||
* from within an extension.
|
||||
*/
|
||||
class IconRegistry implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $fullInitialized = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $tcaInitialized = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $flagsInitialized = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $backendIconsInitialized = false;
|
||||
|
||||
/**
|
||||
* Registered icons
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $icons = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $backendIconDeclaration = 'EXT:core/Resources/Public/Icons/T3Icons/icons.json';
|
||||
|
||||
/**
|
||||
* manually registered icons
|
||||
* hopefully obsolete one day
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $staticIcons = [
|
||||
|
||||
/**
|
||||
* Important Information:
|
||||
*
|
||||
* Icons are maintained in an external repository, if new icons are needed
|
||||
* please request them at: https://github.com/typo3/typo3.icons/issues
|
||||
*/
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping of file extensions to mimetypes
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fileExtensionMapping = [
|
||||
'htm' => 'mimetypes-text-html',
|
||||
'html' => 'mimetypes-text-html',
|
||||
'css' => 'mimetypes-text-css',
|
||||
'js' => 'mimetypes-text-js',
|
||||
'csv' => 'mimetypes-text-csv',
|
||||
'php' => 'mimetypes-text-php',
|
||||
'php6' => 'mimetypes-text-php',
|
||||
'php5' => 'mimetypes-text-php',
|
||||
'php4' => 'mimetypes-text-php',
|
||||
'php3' => 'mimetypes-text-php',
|
||||
'inc' => 'mimetypes-text-php',
|
||||
'ts' => 'mimetypes-text-ts',
|
||||
'typoscript' => 'mimetypes-text-typoscript',
|
||||
'txt' => 'mimetypes-text-text',
|
||||
'class' => 'mimetypes-text-text',
|
||||
'tmpl' => 'mimetypes-text-text',
|
||||
'jpg' => 'mimetypes-media-image',
|
||||
'jpeg' => 'mimetypes-media-image',
|
||||
'gif' => 'mimetypes-media-image',
|
||||
'png' => 'mimetypes-media-image',
|
||||
'bmp' => 'mimetypes-media-image',
|
||||
'tif' => 'mimetypes-media-image',
|
||||
'tiff' => 'mimetypes-media-image',
|
||||
'tga' => 'mimetypes-media-image',
|
||||
'psd' => 'mimetypes-media-image',
|
||||
'eps' => 'mimetypes-media-image',
|
||||
'ai' => 'mimetypes-media-image',
|
||||
'svg' => 'mimetypes-media-image',
|
||||
'pcx' => 'mimetypes-media-image',
|
||||
'avi' => 'mimetypes-media-video',
|
||||
'mpg' => 'mimetypes-media-video',
|
||||
'mpeg' => 'mimetypes-media-video',
|
||||
'mov' => 'mimetypes-media-video',
|
||||
'vimeo' => 'mimetypes-media-video-vimeo',
|
||||
'youtube' => 'mimetypes-media-video-youtube',
|
||||
'wav' => 'mimetypes-media-audio',
|
||||
'mp3' => 'mimetypes-media-audio',
|
||||
'ogg' => 'mimetypes-media-audio',
|
||||
'flac' => 'mimetypes-media-audio',
|
||||
'opus' => 'mimetypes-media-audio',
|
||||
'mid' => 'mimetypes-media-audio',
|
||||
'swf' => 'mimetypes-media-flash',
|
||||
'swa' => 'mimetypes-media-flash',
|
||||
'exe' => 'mimetypes-application',
|
||||
'com' => 'mimetypes-application',
|
||||
't3x' => 'mimetypes-compressed',
|
||||
't3d' => 'mimetypes-compressed',
|
||||
'zip' => 'mimetypes-compressed',
|
||||
'tgz' => 'mimetypes-compressed',
|
||||
'gz' => 'mimetypes-compressed',
|
||||
'pdf' => 'mimetypes-pdf',
|
||||
'doc' => 'mimetypes-word',
|
||||
'dot' => 'mimetypes-word',
|
||||
'docm' => 'mimetypes-word',
|
||||
'docx' => 'mimetypes-word',
|
||||
'dotm' => 'mimetypes-word',
|
||||
'dotx' => 'mimetypes-word',
|
||||
'sxw' => 'mimetypes-word',
|
||||
'rtf' => 'mimetypes-word',
|
||||
'xls' => 'mimetypes-excel',
|
||||
'xlsm' => 'mimetypes-excel',
|
||||
'xlsx' => 'mimetypes-excel',
|
||||
'xltm' => 'mimetypes-excel',
|
||||
'xltx' => 'mimetypes-excel',
|
||||
'sxc' => 'mimetypes-excel',
|
||||
'pps' => 'mimetypes-powerpoint',
|
||||
'ppsx' => 'mimetypes-powerpoint',
|
||||
'ppt' => 'mimetypes-powerpoint',
|
||||
'pptm' => 'mimetypes-powerpoint',
|
||||
'pptx' => 'mimetypes-powerpoint',
|
||||
'potm' => 'mimetypes-powerpoint',
|
||||
'potx' => 'mimetypes-powerpoint',
|
||||
'mount' => 'apps-filetree-mount',
|
||||
'folder' => 'apps-filetree-folder-default',
|
||||
'default' => 'mimetypes-other-other',
|
||||
];
|
||||
|
||||
/**
|
||||
* Mapping of mime types to icons
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $mimeTypeMapping = [
|
||||
'video/*' => 'mimetypes-media-video',
|
||||
'audio/*' => 'mimetypes-media-audio',
|
||||
'image/*' => 'mimetypes-media-image',
|
||||
'text/*' => 'mimetypes-text-text',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $iconAliases = [];
|
||||
|
||||
/**
|
||||
* Array of deprecated icons, add deprecated icons to this array and remove it from registry
|
||||
* - Index of this array contains the deprecated icon
|
||||
* - Value of each entry may contain a possible new identifier
|
||||
*
|
||||
* Example:
|
||||
* [
|
||||
* 'deprecated-icon-identifier' => ['since' => 'TYPO3 v12', 'until' => 'TYPO3 v13', 'replacement' => 'new-icon-identifier'],
|
||||
* 'another-deprecated-identifier' => ['since' => 'TYPO3 v12', 'until' => 'TYPO3 v13', 'replacement' => null],
|
||||
* ]
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedIcons = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultIconIdentifier = 'default-not-found';
|
||||
|
||||
/**
|
||||
* @var FrontendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
private string $cacheIdentifier;
|
||||
|
||||
public function __construct(FrontendInterface $assetsCache, string $cacheIdentifier)
|
||||
{
|
||||
$this->cache = $assetsCache;
|
||||
$this->cacheIdentifier = $cacheIdentifier;
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the registry
|
||||
* This method can be called multiple times, depending on initialization status.
|
||||
* In some cases e.g. TCA is not available, the method must be called multiple times.
|
||||
*/
|
||||
protected function initialize()
|
||||
{
|
||||
if (!$this->backendIconsInitialized) {
|
||||
$this->getCachedBackendIcons();
|
||||
}
|
||||
if (!$this->tcaInitialized && !empty($GLOBALS['TCA'])) {
|
||||
$this->registerTCAIcons();
|
||||
}
|
||||
if (!$this->flagsInitialized) {
|
||||
$this->getCachedFlagIcons();
|
||||
}
|
||||
if ($this->backendIconsInitialized
|
||||
&& $this->tcaInitialized
|
||||
&& $this->flagsInitialized) {
|
||||
$this->fullInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getBackendIconsCacheIdentifier(): string
|
||||
{
|
||||
return $this->cacheIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the icons from cache render them when not cached yet
|
||||
*/
|
||||
protected function getCachedBackendIcons()
|
||||
{
|
||||
$cacheIdentifier = $this->getBackendIconsCacheIdentifier();
|
||||
$cacheEntry = $this->cache->get($cacheIdentifier);
|
||||
|
||||
if ($cacheEntry !== false) {
|
||||
$this->icons = $cacheEntry;
|
||||
} else {
|
||||
$this->registerBackendIcons();
|
||||
// all found icons should now be present, for historic reasons now merge w/ the statically declared icons
|
||||
$this->icons = array_merge($this->icons, $this->iconAliases, $this->staticIcons);
|
||||
$this->cache->set($cacheIdentifier, $this->icons);
|
||||
}
|
||||
// if there's now at least one icon registered, consider it successful
|
||||
if (is_array($this->icons) && (count($this->icons) >= count($this->staticIcons))) {
|
||||
$this->backendIconsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Automatically find and register the core backend icons
|
||||
*/
|
||||
protected function registerBackendIcons(): void
|
||||
{
|
||||
$dir = dirname($this->backendIconDeclaration);
|
||||
$absoluteIconDeclarationPath = GeneralUtility::getFileAbsFileName($this->backendIconDeclaration);
|
||||
$json = json_decode(file_get_contents($absoluteIconDeclarationPath) ?: '', true);
|
||||
foreach ($json['icons'] ?? [] as $declaration) {
|
||||
$iconOptions = [
|
||||
'sprite' => $dir . '/' . $declaration['sprite'],
|
||||
];
|
||||
// kind of hotfix for now, needs a nicer concept later
|
||||
if ($declaration['category'] === 'spinner') {
|
||||
$iconOptions['spinning'] = true;
|
||||
}
|
||||
if (isset($declaration['bidi']) && $declaration['bidi'] === true) {
|
||||
$iconOptions['bidi'] = true;
|
||||
}
|
||||
|
||||
$this->registerIcon(
|
||||
$declaration['identifier'],
|
||||
SvgSpriteIconProvider::class,
|
||||
$iconOptions
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($json['aliases'] as $alias => $identifier) {
|
||||
$this->registerAlias($alias, $identifier);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @return bool
|
||||
*/
|
||||
public function isRegistered($identifier)
|
||||
{
|
||||
if (!$this->fullInitialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
return isset($this->icons[$identifier]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $identifier
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeprecated($identifier)
|
||||
{
|
||||
return isset($this->deprecatedIcons[$identifier]);
|
||||
}
|
||||
|
||||
public function getDefaultIconIdentifier(): string
|
||||
{
|
||||
return $this->defaultIconIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an icon to be available inside the Icon Factory
|
||||
*
|
||||
* @param string $identifier
|
||||
* @param string $iconProviderClassName
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function registerIcon($identifier, $iconProviderClassName, array $options = [])
|
||||
{
|
||||
if (!in_array(IconProviderInterface::class, class_implements($iconProviderClassName) ?: [], true)) {
|
||||
throw new \InvalidArgumentException('An IconProvider must implement '
|
||||
. IconProviderInterface::class, 1437425803);
|
||||
}
|
||||
$this->icons[$identifier] = [
|
||||
'provider' => $iconProviderClassName,
|
||||
'options' => $options,
|
||||
];
|
||||
|
||||
if (isset($options['deprecated'])) {
|
||||
$this->deprecatedIcons[$identifier] = $options['deprecated'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an icon to be available inside the Icon Factory
|
||||
*
|
||||
* @param string $alias
|
||||
* @param string $identifier
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function registerAlias($alias, $identifier)
|
||||
{
|
||||
if (!isset($this->icons[$identifier])) {
|
||||
throw new \InvalidArgumentException('No icon with identifier "' . $identifier . '" registered.', 1602251838);
|
||||
}
|
||||
$this->iconAliases[$alias] = $this->icons[$identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an icon for a file extension
|
||||
*
|
||||
* @param string $fileExtension
|
||||
* @param string $iconIdentifier
|
||||
*/
|
||||
public function registerFileExtension($fileExtension, $iconIdentifier)
|
||||
{
|
||||
$this->fileExtensionMapping[$fileExtension] = $iconIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an icon for a mime-type
|
||||
*
|
||||
* @param string $mimeType
|
||||
* @param string $iconIdentifier
|
||||
*/
|
||||
public function registerMimeTypeIcon($mimeType, $iconIdentifier)
|
||||
{
|
||||
$this->mimeTypeMapping[$mimeType] = $iconIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the configuration provided by registerIcon()
|
||||
*
|
||||
* @param string $identifier the icon identifier
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getIconConfigurationByIdentifier($identifier)
|
||||
{
|
||||
if (!$this->fullInitialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
if ($this->isDeprecated($identifier)) {
|
||||
$deprecation = $this->deprecatedIcons[$identifier];
|
||||
$since = $deprecation['since'] ?? null;
|
||||
$until = $deprecation['until'] ?? null;
|
||||
$replacement = $deprecation['replacement'] ?? null;
|
||||
|
||||
$message = 'The icon "%s" is deprecated%s%s.';
|
||||
$arguments = [
|
||||
$identifier,
|
||||
$since !== null ? ' since ' . $since : '',
|
||||
$until !== null ? ' and will be removed in ' . $until : '',
|
||||
];
|
||||
|
||||
if ($replacement) {
|
||||
$message .= ' Please use "%s" instead.';
|
||||
$arguments[] = $replacement;
|
||||
}
|
||||
trigger_error(vsprintf($message, $arguments), E_USER_DEPRECATED);
|
||||
}
|
||||
if (!$this->isRegistered($identifier)) {
|
||||
throw new Exception('Icon with identifier "' . $identifier . '" is not registered"', 1437425804);
|
||||
}
|
||||
return $this->icons[$identifier];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllRegisteredIconIdentifiers()
|
||||
{
|
||||
if (!$this->fullInitialized) {
|
||||
$this->initialize();
|
||||
}
|
||||
return array_keys($this->icons);
|
||||
}
|
||||
|
||||
public function getDeprecatedIcons(): array
|
||||
{
|
||||
return $this->deprecatedIcons;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fileExtension
|
||||
* @return string
|
||||
*/
|
||||
public function getIconIdentifierForFileExtension($fileExtension)
|
||||
{
|
||||
// If the file extension is not valid use the default one
|
||||
if (!isset($this->fileExtensionMapping[$fileExtension])) {
|
||||
$fileExtension = 'default';
|
||||
}
|
||||
return $this->fileExtensionMapping[$fileExtension];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iconIdentifier for given mimeType
|
||||
*
|
||||
* @param string $mimeType
|
||||
* @return string|null Returns null if no icon is registered for the mimeType
|
||||
*/
|
||||
public function getIconIdentifierForMimeType($mimeType)
|
||||
{
|
||||
if (!isset($this->mimeTypeMapping[$mimeType])) {
|
||||
return null;
|
||||
}
|
||||
return $this->mimeTypeMapping[$mimeType];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load icons from TCA for each table and add them as "tcarecords-XX" to $this->icons
|
||||
*/
|
||||
protected function registerTCAIcons()
|
||||
{
|
||||
$resultArray = [];
|
||||
|
||||
$tcaTables = array_keys($GLOBALS['TCA'] ?? []);
|
||||
// check every table in the TCA, if an icon is needed
|
||||
foreach ($tcaTables as $tableName) {
|
||||
// This method is only needed for TCA tables where typeicon_classes are not configured
|
||||
$iconIdentifier = 'tcarecords-' . $tableName . '-default';
|
||||
if (
|
||||
isset($this->icons[$iconIdentifier])
|
||||
|| !isset($GLOBALS['TCA'][$tableName]['ctrl']['iconfile'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$resultArray[$iconIdentifier] = $GLOBALS['TCA'][$tableName]['ctrl']['iconfile'];
|
||||
}
|
||||
|
||||
foreach ($resultArray as $iconIdentifier => $iconFilePath) {
|
||||
$iconProviderClass = $this->detectIconProvider($iconFilePath);
|
||||
$this->icons[$iconIdentifier] = [
|
||||
'provider' => $iconProviderClass,
|
||||
'options' => [
|
||||
'source' => $iconFilePath,
|
||||
],
|
||||
];
|
||||
}
|
||||
$this->tcaInitialized = true;
|
||||
}
|
||||
|
||||
protected function getCachedFlagIcons(): void
|
||||
{
|
||||
$cacheIdentifier = $this->getBackendIconsCacheIdentifier() . '_flags';
|
||||
$cacheEntry = $this->cache->get($cacheIdentifier);
|
||||
|
||||
if ($cacheEntry === false) {
|
||||
$cacheEntry = $this->registerFlags();
|
||||
$this->cache->set($cacheIdentifier, $cacheEntry);
|
||||
}
|
||||
$this->icons = array_merge($this->icons, $cacheEntry);
|
||||
// if there's now at least one icon registered, consider it successful
|
||||
if (is_array($cacheEntry) && $cacheEntry !== []) {
|
||||
$this->flagsInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register flags
|
||||
*/
|
||||
protected function registerFlags(): array
|
||||
{
|
||||
$iconFolder = 'EXT:core/Resources/Public/Icons/Flags/';
|
||||
$folderPath = GeneralUtility::getFileAbsFileName($iconFolder);
|
||||
$flagIcons = [];
|
||||
|
||||
if ($handle = opendir($folderPath)) {
|
||||
while (($file = readdir($handle)) !== false) {
|
||||
$fileInfo = pathinfo($folderPath . $file);
|
||||
if ($fileInfo['extension'] !== 'webp') {
|
||||
continue;
|
||||
}
|
||||
$flagIcons['flags-' . strtolower($fileInfo['filename'])] = [
|
||||
'provider' => BitmapIconProvider::class,
|
||||
'options' => [
|
||||
'source' => $iconFolder . $file,
|
||||
],
|
||||
];
|
||||
}
|
||||
closedir($handle);
|
||||
}
|
||||
|
||||
return $flagIcons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the IconProvider of an icon
|
||||
*
|
||||
* @param string $iconReference
|
||||
* @return string
|
||||
*/
|
||||
public function detectIconProvider($iconReference)
|
||||
{
|
||||
if (str_ends_with(strtolower((string)$iconReference), 'svg')) {
|
||||
return SvgIconProvider::class;
|
||||
}
|
||||
return BitmapIconProvider::class;
|
||||
}
|
||||
|
||||
public function warmupCaches(CacheWarmupEvent $event): void
|
||||
{
|
||||
if ($event->hasGroup('system')) {
|
||||
$backupIcons = $this->icons;
|
||||
$backupAliases = $this->iconAliases;
|
||||
$this->icons = [];
|
||||
$this->iconAliases = [];
|
||||
|
||||
$this->registerBackendIcons();
|
||||
// all found icons should now be present, for historic reasons now merge w/ the statically declared icons
|
||||
$this->icons = array_merge($this->icons, $this->iconAliases, $this->staticIcons);
|
||||
$this->cache->set($this->getBackendIconsCacheIdentifier(), $this->icons);
|
||||
|
||||
$this->icons = $backupIcons;
|
||||
$this->iconAliases = $backupAliases;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user