228 lines
10 KiB
PHP
228 lines
10 KiB
PHP
<?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\Extbase\Utility;
|
|
|
|
use TYPO3\CMS\Core\Schema\Struct\SelectItem;
|
|
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
|
|
/**
|
|
* Utilities to manage plugins and modules of an extension. Also useful to auto-generate the autoloader registry
|
|
* file ext_autoload.php.
|
|
*/
|
|
class ExtensionUtility
|
|
{
|
|
public const PLUGIN_TYPE_CONTENT_ELEMENT = 'CType';
|
|
|
|
/**
|
|
* Add auto-generated TypoScript to configure the Extbase Dispatcher.
|
|
*
|
|
* When adding a frontend plugin you will have to add both an entry to the TCA definition
|
|
* of tt_content table AND to the TypoScript template which must initiate the rendering.
|
|
* Including the plugin code after "defaultContentRendering" adds the necessary TypoScript
|
|
* for calling the appropriate controller and action of your plugin.
|
|
* FOR USE IN ext_localconf.php FILES
|
|
* Usage: 2
|
|
*
|
|
* @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
|
|
* @param string $pluginName must be a unique id for your plugin in UpperCamelCase (the string length of the extension key added to the length of the plugin name should be less than 32!)
|
|
* @param array $controllerActions is an array of allowed combinations of controller and action stored in an array (controller name as key and a comma separated list of action names as value, the first controller and its first action is chosen as default)
|
|
* @param array $nonCacheableControllerActions is an optional array of controller name and action names which should not be cached (array as defined in $controllerActions)
|
|
* @param string|null $pluginType must be omitted or set to \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT - actually unused in the method, for b/w compatibility only
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public static function configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = [], ?string $pluginType = null): void
|
|
{
|
|
self::checkPluginNameFormat($pluginName);
|
|
self::checkExtensionNameFormat($extensionName);
|
|
|
|
$pluginType ??= self::PLUGIN_TYPE_CONTENT_ELEMENT;
|
|
if ($pluginType !== self::PLUGIN_TYPE_CONTENT_ELEMENT) {
|
|
throw new \InvalidArgumentException('Fifth parameter $pluginType has to be omitted or set to "CType"', 1730801526);
|
|
}
|
|
|
|
$extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
|
|
$pluginSignature = strtolower($extensionName . '_' . $pluginName);
|
|
|
|
$controllerActions = self::actionCommaListToArray($controllerActions);
|
|
$nonCacheableControllerActions = self::actionCommaListToArray($nonCacheableControllerActions);
|
|
self::registerControllerActions($extensionName, $pluginName, $controllerActions, $nonCacheableControllerActions);
|
|
|
|
$pluginConfiguration = trim('
|
|
tt_content.' . $pluginSignature . ' =< lib.contentElement
|
|
tt_content.' . $pluginSignature . ' {
|
|
templateName = Generic
|
|
20 = EXTBASEPLUGIN
|
|
20 {
|
|
extensionName = ' . $extensionName . '
|
|
pluginName = ' . $pluginName . '
|
|
}
|
|
}');
|
|
|
|
ExtensionManagementUtility::addTypoScript($extensionName, 'setup', '
|
|
# Setting ' . $extensionName . ' plugin TypoScript
|
|
' . $pluginConfiguration, 'defaultContentRendering');
|
|
}
|
|
|
|
/**
|
|
* @param array<string, string[]> $controllerActions
|
|
* @param array<string, string[]> $nonCacheableControllerActions
|
|
* @internal
|
|
*/
|
|
public static function registerControllerActions(string $extensionName, string $pluginName, array $controllerActions, array $nonCacheableControllerActions): void
|
|
{
|
|
if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] ?? false)) {
|
|
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = [];
|
|
}
|
|
foreach ($controllerActions as $controllerClassName => $actionsList) {
|
|
$controllerAlias = self::resolveControllerAliasFromControllerClassName($controllerClassName);
|
|
|
|
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName] = [
|
|
'className' => $controllerClassName,
|
|
'alias' => $controllerAlias,
|
|
'actions' => $actionsList,
|
|
];
|
|
|
|
if (!empty($nonCacheableControllerActions[$controllerClassName])) {
|
|
$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions']
|
|
= $nonCacheableControllerActions[$controllerClassName];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register an Extbase PlugIn into backend's list of plugins
|
|
* FOR USE IN Configuration/TCA/Overrides/tt_content.php
|
|
*
|
|
* @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
|
|
* @param string $pluginName must be a unique id for your plugin in UpperCamelCase (the string length of the extension key added to the length of the plugin name should be less than 32!)
|
|
* @param string $pluginTitle is a speaking title of the plugin that will be displayed in the drop down menu in the backend
|
|
* @param string|null $pluginIcon is an icon identifier or file path prepended with "EXT:", that will be displayed in the drop down menu in the backend (optional)
|
|
* @param string $group add this plugin to a plugin group, should be something like "news" or the like, "plugins" as regular
|
|
* @param string $pluginDescription additional description
|
|
* @param string $flexForm The flex form (data structure) to be used for the plugin. Either a reference to a flex-form XML file (eg. "FILE:EXT:newloginbox/flexform_ds.xml") or the XML directly.
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
public static function registerPlugin($extensionName, $pluginName, $pluginTitle, $pluginIcon = null, $group = 'plugins', string $pluginDescription = '', string $flexForm = ''): string
|
|
{
|
|
self::checkPluginNameFormat($pluginName);
|
|
self::checkExtensionNameFormat($extensionName);
|
|
|
|
$extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
|
|
$pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
|
|
|
|
ExtensionManagementUtility::addPlugin(
|
|
new SelectItem(
|
|
'select',
|
|
// set pluginName as default pluginTitle
|
|
$pluginTitle ?: $pluginName,
|
|
$pluginSignature,
|
|
$pluginIcon ?? 'content-plugin',
|
|
$group,
|
|
$pluginDescription
|
|
),
|
|
$flexForm
|
|
);
|
|
return $pluginSignature;
|
|
}
|
|
|
|
/**
|
|
* @internal only used for TYPO3 Core
|
|
*/
|
|
public static function resolveControllerAliasFromControllerClassName(string $controllerClassName): string
|
|
{
|
|
// This method has been adjusted for TYPO3 10.3 to mitigate the issue that controller aliases
|
|
// could not longer be calculated from controller classes when calling
|
|
// \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin().
|
|
//
|
|
// The idea for version 11 is to let the user choose a controller alias and to check for its
|
|
// uniqueness per plugin. That way, the core does no longer rely on the namespace of
|
|
// controller classes to be in a specific format.
|
|
//
|
|
// todo: Change the way plugins are registered and enforce a controller alias to be set by
|
|
// the user to also free the core from guessing a simple alias by looking at the
|
|
// class name. This makes it possible to choose controller class names without a
|
|
// controller suffix.
|
|
|
|
$strLen = strlen('Controller');
|
|
|
|
if (!str_ends_with($controllerClassName, 'Controller')) {
|
|
return '';
|
|
}
|
|
|
|
$controllerClassNameWithoutControllerSuffix = substr($controllerClassName, 0, -$strLen);
|
|
|
|
if (strrpos($controllerClassNameWithoutControllerSuffix, 'Controller\\') === false) {
|
|
$positionOfLastSlash = (int)strrpos($controllerClassNameWithoutControllerSuffix, '\\');
|
|
$positionOfLastSlash += $positionOfLastSlash === 0 ? 0 : 1;
|
|
|
|
return substr($controllerClassNameWithoutControllerSuffix, $positionOfLastSlash);
|
|
}
|
|
|
|
$positionOfControllerNamespacePart = (int)strrpos(
|
|
$controllerClassNameWithoutControllerSuffix,
|
|
'Controller\\'
|
|
);
|
|
|
|
return substr(
|
|
$controllerClassNameWithoutControllerSuffix,
|
|
$positionOfControllerNamespacePart + $strLen + 1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param array<string, string|string[]> $controllerActions
|
|
* @return array<string, string[]>
|
|
*/
|
|
protected static function actionCommaListToArray(array $controllerActions): array
|
|
{
|
|
foreach ($controllerActions as $controllerClassName => $actionsList) {
|
|
if (is_array($actionsList)) {
|
|
continue;
|
|
}
|
|
$actionsListArray = GeneralUtility::trimExplode(',', (string)$actionsList);
|
|
$controllerActions[$controllerClassName] = $actionsListArray;
|
|
}
|
|
return $controllerActions;
|
|
}
|
|
|
|
/**
|
|
* Check a given extension name for validity.
|
|
*
|
|
* @param string $extensionName The name of the extension
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
protected static function checkExtensionNameFormat($extensionName)
|
|
{
|
|
if (empty($extensionName)) {
|
|
throw new \InvalidArgumentException('The extension name must not be empty', 1239891990);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check a given plugin name for validity.
|
|
*
|
|
* @param string $pluginName The name of the plugin
|
|
* @throws \InvalidArgumentException
|
|
*/
|
|
protected static function checkPluginNameFormat($pluginName)
|
|
{
|
|
if (empty($pluginName)) {
|
|
throw new \InvalidArgumentException('The plugin name must not be empty', 1239891988);
|
|
}
|
|
}
|
|
}
|