Commit 241497a2 authored by Benni Mack's avatar Benni Mack Committed by Frank Nägler
Browse files

[TASK] Unify references for backend modules icons

Somehow on earth, regular (non-extbase) modules
register their module icons via [labels][tabs_images][tab]
when using addModule(). The icon is not related to the
labels at all, and can be simplified.

All icons registered are available via the module
configuration option "icon" and old formats are migrated
automatically (with a deprecation warning), and always
stored in an absolute format.

Using "icon" now in the available places makes it easier
and is unified in ModuleLoader, FormEngine, EXT:about and the
Module Menu.

Resolves: #72827
Releases: master
Change-Id: Ie5770539bcb94921a9319a1d8d53d981c49fbcd8
Reviewed-on: https://review.typo3.org/46100

Reviewed-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
Tested-by: Frank Nägler's avatarFrank Nägler <frank.naegler@typo3.org>
parent 683625fc
......@@ -144,7 +144,7 @@ class ModulesController extends ActionController
$subModuleKey = $moduleName . '_' . $subModuleName . '_tab';
$subModuleData = array();
$subModuleData['name'] = $subModuleName;
$subModuleData['icon'] = PathUtility::stripPathSitePrefix($this->languageService->moduleLabels['tabs_images'][$subModuleKey]);
$subModuleData['icon'] = PathUtility::getAbsoluteWebPath($subModuleInfo['icon']);
$subModuleData['label'] = $this->languageService->moduleLabels['tabs'][$subModuleKey];
$subModuleData['shortDescription'] = $this->languageService->moduleLabels['labels'][$subModuleKey . 'label'];
$subModuleData['longDescription'] = $this->languageService->moduleLabels['labels'][$subModuleKey . 'descr'];
......
......@@ -74,18 +74,21 @@ class ShortcutToolbarItem implements ToolbarItemInterface
*/
protected $iconFactory;
/**
* @var ModuleLoader
*/
protected $moduleLoader;
/**
* Constructor
*/
public function __construct()
{
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
$this->getLanguageService()->includeLLFile('EXT:lang/locallang_misc.xlf');
// Needed to get the correct icons when reloading the menu after saving it
$loadModules = GeneralUtility::makeInstance(ModuleLoader::class);
$loadModules->load($GLOBALS['TBE_MODULES']);
}
$this->getLanguageService()->includeLLFile('EXT:lang/locallang_misc.xlf');
// Needed to get the correct icons when reloading the menu after saving it
$this->moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
$this->moduleLoader->load($GLOBALS['TBE_MODULES']);
// By default, 5 groups are set
$this->shortcutGroups = array(
......@@ -275,7 +278,8 @@ class ShortcutToolbarItem implements ToolbarItemInterface
}
// Check for module access
$moduleName = $row['M_module_name'] ?: $row['module_name'];
if (!isset($this->getLanguageService()->moduleLabels['tabs_images'][$moduleName . '_tab'])) {
if (!isset($this->getLanguageService()->moduleLabels['labels'][$moduleName . '_tablabel'])) {
// Nice hack to check if the user has access to this module
// - otherwise the translation label would not have been loaded :-)
continue;
......@@ -840,14 +844,15 @@ class ShortcutToolbarItem implements ToolbarItemInterface
$icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIcon('mimetypes-word', Icon::SIZE_SMALL)->render() . '</span>';
break;
default:
if ($languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab']) {
$icon = $languageService->moduleLabels['tabs_images'][$row['module_name'] . '_tab'];
// Change icon of fileadmin references - otherwise it doesn't differ with Web->List
$icon = str_replace('mod/file/list/list.gif', 'mod/file/file.gif', $icon);
if (GeneralUtility::isAbsPath($icon)) {
$icon = '../' . PathUtility::stripPathSitePrefix($icon);
}
// @todo: hardcoded width as we don't have a way to address module icons with an API yet.
$moduleName = $row['module_name'];
if (strpos($moduleName, '_') !== false) {
list($mainModule, $subModule) = explode('_', $moduleName, 2);
$icon = $this->moduleLoader->modules[$mainModule]['sub'][$subModule]['icon'];
} elseif (!empty($moduleName)) {
$icon = $this->moduleLoader->modules[$moduleName]['icon'];
}
if (!empty($icon) && file_exists($icon)) {
$icon = '../' . PathUtility::stripPathSitePrefix($icon);
$icon = '<img src="' . htmlspecialchars($icon) . '" alt="' . $titleAttribute . '" width="16">';
} else {
$icon = '<span title="' . $titleAttribute . '">' . $this->iconFactory->getIcon('empty-empty', Icon::SIZE_SMALL)->render() . '</span>';
......
......@@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* Repository for backend module menu
......@@ -353,47 +354,11 @@ class BackendModuleRepository implements \TYPO3\CMS\Core\SingletonInterface
if (!empty($moduleData['iconIdentifier'])) {
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$icon = $iconFactory->getIcon($moduleData['iconIdentifier'])->render();
} elseif (!empty($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey])) {
$imageReference = $GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey];
$iconFileRelative = $this->getModuleIconRelative($imageReference);
if (!empty($iconFileRelative)) {
$iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
$iconSizes = @getimagesize($this->getModuleIconAbsolute($imageReference));
$icon = '<img src="' . $iconFileRelative . '" ' . $iconSizes[3] . ' title="' . htmlspecialchars($iconTitle) . '" alt="' . htmlspecialchars($iconTitle) . '" />';
}
} elseif (isset($moduleData['icon']) && is_file($moduleData['icon'])) {
$iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
$iconSizes = @getimagesize($moduleData['icon']);
$icon = '<img src="' . PathUtility::getAbsoluteWebPath($moduleData['icon']) . '" ' . $iconSizes[3] . ' title="' . htmlspecialchars($iconTitle) . '" alt="' . htmlspecialchars($iconTitle) . '" />';
}
return $icon;
}
/**
* Returns the filename readable for the script from PATH_typo3.
* That means absolute names are just returned while relative names are
* prepended with the path pointing back to typo3/ dir
*
* @param string $iconFilename Icon filename
* @return string Icon filename with absolute path
* @see getModuleIconRelative()
*/
protected function getModuleIconAbsolute($iconFilename)
{
if (!GeneralUtility::isAbsPath($iconFilename)) {
$iconFilename = PATH_typo3 . $iconFilename;
}
return $iconFilename;
}
/**
* Returns relative path to the icon filename for use in img-tags
*
* @param string $iconFilename Icon filename
* @return string Icon filename with relative path
* @see getModuleIconAbsolute()
*/
protected function getModuleIconRelative($iconFilename)
{
if (GeneralUtility::isAbsPath($iconFilename)) {
$iconFilename = '../' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($iconFilename);
}
return $iconFilename;
}
}
......@@ -310,7 +310,12 @@ abstract class AbstractItemProvider
if (is_array($modList)) {
foreach ($modList as $theMod) {
// Icon:
$icon = $languageService->moduleLabels['tabs_images'][$theMod . '_tab'];
if (strpos($theMod, '_') !== false) {
list($mainModule, $subModule) = explode('_', $theMod, 2);
$icon = $loadModules->modules[$mainModule]['sub'][$subModule]['icon'];
} else {
$icon = $loadModules->modules[$theMod]['icon'];
}
if ($icon) {
$icon = '../' . PathUtility::stripPathSitePrefix($icon);
}
......
......@@ -168,18 +168,8 @@ class ModuleLoader
// Language processing. This will add module labels and image reference to the internal ->moduleLabels array of the LANG object.
$lang = $this->getLanguageService();
if (is_object($lang)) {
// $setupInformation['labels']['default']['tabs_images']['tab'] is for modules the reference
// to the module icon.
$defaultLabels = $setupInformation['labels']['default'];
// Here the path is transformed to an absolute reference.
if ($defaultLabels['tabs_images']['tab']) {
if (\TYPO3\CMS\Core\Utility\StringUtility::beginsWith($defaultLabels['tabs_images']['tab'], 'EXT:')) {
list($extensionKey, $relativePath) = explode('/', substr($defaultLabels['tabs_images']['tab'], 4), 2);
$defaultLabels['tabs_images']['tab'] = ExtensionManagementUtility::extPath($extensionKey) . $relativePath;
}
}
// If LOCAL_LANG references are used for labels of the module:
if ($defaultLabels['ll_ref']) {
// Now the 'default' key is loaded with the CURRENT language - not the english translation...
......
......@@ -1059,9 +1059,6 @@ class TcaSelectItemsTest extends UnitTestCase
$GLOBALS['LANG'] = $languageService->reveal();
$languageService->sL(Argument::cetera())->willReturnArgument(0);
$languageService->moduleLabels = [
'tabs_images' => [
'aModule_tab' => PATH_site . 'aModuleTabIcon.gif',
],
'labels' => [
'aModule_tablabel' => 'aModuleTabLabel',
'aModule_tabdescr' => 'aModuleTabDescription',
......@@ -1078,6 +1075,11 @@ class TcaSelectItemsTest extends UnitTestCase
$moduleLoaderProphecy->modListGroup = [
'aModule',
];
$moduleLoaderProphecy->modules = [
'aModule' => [
'icon' => PATH_site . 'aModuleTabIcon.gif'
]
];
$expectedItems = [
0 => [
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Backend\Controller\PageLayoutController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_layout',
'icon' => 'EXT:backend/Resources/Public/Icons/module-page.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:backend/Resources/Public/Icons/module-page.svg',
),
'll_ref' => 'LLL:EXT:backend/Resources/Private/Language/locallang_mod.xlf',
),
)
......
......@@ -865,15 +865,17 @@ class ExtensionManagementUtility
public static function configureModule($moduleSignature)
{
$moduleConfiguration = $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature];
$iconPathAndFilename = $moduleConfiguration['icon'];
if (substr($iconPathAndFilename, 0, 4) === 'EXT:') {
list($extensionKey, $relativePath) = explode('/', substr($iconPathAndFilename, 4), 2);
$iconPathAndFilename = self::extPath($extensionKey) . $relativePath;
if (!empty($moduleConfiguration['labels']['tabs_images']['tab'])) {
GeneralUtility::deprecationLog('Module registration for backend module "' . $moduleSignature . '" uses old referencing for the icon. Use the configuration option "icon" directly instead of [labels][tabs_images][tab]. The old option is removed with TYPO3 v9.');
$moduleConfiguration['icon'] = $moduleConfiguration['labels']['tabs_images']['tab'];
unset($moduleConfiguration['labels']['tabs_images']['tab']);
}
if (!empty($moduleConfiguration['icon'])) {
$moduleConfiguration['icon'] = GeneralUtility::getFileAbsFileName($moduleConfiguration['icon'], false, true);
}
$moduleLabels = array(
'tabs_images' => array(
'tab' => $iconPathAndFilename
),
'labels' => array(
'tablabel' => $GLOBALS['LANG']->sL($moduleConfiguration['labels'] . ':mlang_labels_tablabel'),
'tabdescr' => $GLOBALS['LANG']->sL($moduleConfiguration['labels'] . ':mlang_labels_tabdescr')
......@@ -934,6 +936,17 @@ class ExtensionManagementUtility
// add additional configuration
if (is_array($moduleConfiguration) && !empty($moduleConfiguration)) {
$fullModuleSignature = $main . ($sub ? '_' . $sub : '');
if (!empty($moduleConfiguration['labels']['tabs_images']['tab'])) {
GeneralUtility::deprecationLog('Module registration for module "' . $fullModuleSignature . '" uses old referencing for the icon. Use the configuration option "icon" directly instead of [labels][tabs_images][tab]. The old option is removed with TYPO3 v9.');
$moduleConfiguration['icon'] = $moduleConfiguration['labels']['tabs_images']['tab'];
unset($moduleConfiguration['labels']['tabs_images']['tab']);
}
if (!empty($moduleConfiguration['icon'])) {
$moduleConfiguration['icon'] = GeneralUtility::getFileAbsFileName($moduleConfiguration['icon'], false, true);
}
$GLOBALS['TBE_MODULES']['_configuration'][$fullModuleSignature] = $moduleConfiguration;
}
}
......
==============================================================================
Deprecation: #72827 - Module Icon configuration via [labels][tabs_images][tab]
==============================================================================
Description
===========
When registering a non-extbase module, the option to configure the icon was previously done with the module
configuration option ``[labels][tabs_images][tab]``. To clarify the naming, the property "icon" is introduced which
expects a reference to the icon via the ``EXT:myextension/path/to/the/file.png`` syntax.
The old option ``[labels][tabs_images][tab]`` was deprecated.
Impact
======
When using the old configuration property ``[labels][tabs_images][tab]``, a deprecation message is thrown.
Affected Installations
======================
Installations with custom backend non-extbase modules of third-party-extensions that still use the old configuration
property.
Migration
=========
Replace the ``[labels][tabs_images][tab]`` with ``[icon]`` in ``ext_tables.php`` in your extension like this:
.. code-block:: php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule(
'system',
'dbint',
'',
'',
array(
'routeTarget' => \TYPO3\CMS\Lowlevel\View\DatabaseIntegrityView::class . '::mainAction',
'access' => 'admin',
'name' => 'system_dbint',
'workspaces' => 'online',
'icon' => 'EXT:lowlevel/Resources/Public/Icons/module-dbint.svg',
'labels' => array(
'll_ref' => 'LLL:EXT:lowlevel/Resources/Private/Language/locallang_mod.xlf',
),
)
);
\ No newline at end of file
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Dbal\Controller\ModuleController::class . '::mainAction',
'access' => 'admin',
'name' => 'tools_txdbalM1',
'icon' => 'EXT:dbal/Resources/Public/Icons/module-dbal.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:dbal/Resources/Public/Icons/module-dbal.svg',
),
'll_ref' => 'LLL:EXT:dbal/Resources/Private/Language/locallang_mod.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Func\Controller\PageFunctionsController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_func',
'icon' => 'EXT:func/Resources/Public/Icons/module-func.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:func/Resources/Public/Icons/module-func.svg',
),
'll_ref' => 'LLL:EXT:lang/locallang_mod_web_func.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Info\Controller\InfoModuleController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_info',
'icon' => 'EXT:info/Resources/Public/Icons/module-info.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:info/Resources/Public/Icons/module-info.svg',
),
'll_ref' => 'LLL:EXT:lang/locallang_mod_web_info.xlf',
),
)
......
......@@ -20,10 +20,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Install\Controller\BackendModuleController::class . '::index',
'access' => 'admin',
'name' => 'system_extinstall',
'icon' => 'EXT:install/Resources/Public/Icons/module-install.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:install/Resources/Public/Icons/module-install.svg',
),
'll_ref' => 'LLL:EXT:install/Resources/Private/Language/BackendModule.xlf',
),
)
......
......@@ -12,10 +12,8 @@ if (TYPO3_MODE === 'BE') {
'access' => 'admin',
'name' => 'system_dbint',
'workspaces' => 'online',
'icon' => 'EXT:lowlevel/Resources/Public/Icons/module-dbint.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:lowlevel/Resources/Public/Icons/module-dbint.svg',
),
'll_ref' => 'LLL:EXT:lowlevel/Resources/Private/Language/locallang_mod.xlf',
),
)
......@@ -30,10 +28,8 @@ if (TYPO3_MODE === 'BE') {
'access' => 'admin',
'name' => 'system_config',
'workspaces' => 'online',
'icon' => 'EXT:lowlevel/Resources/Public/Icons/module-config.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:lowlevel/Resources/Public/Icons/module-config.svg',
),
'll_ref' => 'LLL:EXT:lowlevel/Resources/Private/Language/locallang_mod_configuration.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Recordlist\RecordList::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_list',
'icon' => 'EXT:recordlist/Resources/Public/Icons/module-list.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:recordlist/Resources/Public/Icons/module-list.svg',
),
'll_ref' => 'LLL:EXT:lang/locallang_mod_web_list.xlf',
),
)
......
......@@ -12,10 +12,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController::class . '::mainAction',
'access' => 'admin',
'name' => 'system_txschedulerM1',
'icon' => 'EXT:scheduler/Resources/Public/Icons/module-scheduler.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:scheduler/Resources/Public/Icons/module-scheduler.svg',
),
'll_ref' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang_mod.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Setup\Controller\SetupModuleController::class . '::mainAction',
'access' => 'group,user',
'name' => 'user_setup',
'icon' => 'EXT:setup/Resources/Public/Icons/module-setup.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:setup/Resources/Public/Icons/module-setup.svg',
),
'll_ref' => 'LLL:EXT:setup/Resources/Private/Language/locallang_mod.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Taskcenter\Controller\TaskModuleController::class . '::mainAction',
'access' => 'group,user',
'name' => 'user_task',
'icon' => 'EXT:taskcenter/Resources/Public/Icons/module-taskcenter.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:taskcenter/Resources/Public/Icons/module-taskcenter.svg',
),
'll_ref' => 'LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf',
),
)
......
......@@ -11,10 +11,8 @@ if (TYPO3_MODE === 'BE') {
'routeTarget' => \TYPO3\CMS\Tstemplate\Controller\TypoScriptTemplateModuleController::class . '::mainAction',
'access' => 'admin',
'name' => 'web_ts',
'icon' => 'EXT:tstemplate/Resources/Public/Icons/module-tstemplate.svg',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:tstemplate/Resources/Public/Icons/module-tstemplate.svg',
),
'll_ref' => 'LLL:EXT:tstemplate/Resources/Private/Language/locallang_mod.xlf',
),
)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment