[TASK] Protect TaskModuleController Part 2 39/58239/3
authorChristian Kuhn <lolli@schwarzbu.ch>
Sun, 9 Sep 2018 11:06:22 +0000 (13:06 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 10 Sep 2018 11:36:13 +0000 (13:36 +0200)
TaskModuleController no longer extends BaseScriptClass. Properties
and methods are copied over to the controller and most of them are
set to protected. An instance of the controller class is given
to single class that fetch and set the one or the other information,
so not everything can be made protected. The patch should still
allow us to have more refactoring freedom in v10 in this area.

Resolves: #86204
Related: #86182
Releases: master
Change-Id: I171b22ddbcee67205234b6ca23acf9c95da6ac1b
Reviewed-on: https://review.typo3.org/58239
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Frank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Naegler <frank.naegler@typo3.org>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/backend/Classes/Module/AbstractFunctionModule.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedMethodsInTaskModuleController.rst [deleted file]
typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedTaskModuleController.rst [new file with mode: 0644]
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
typo3/sysext/sys_action/Classes/ActionTask.php
typo3/sysext/taskcenter/Classes/Controller/TaskModuleController.php

index 66b5bd7..3f32ef8 100644 (file)
@@ -112,7 +112,7 @@ abstract class AbstractFunctionModule
     /**
      * Initialize the object
      *
-     * @param BaseScriptClass $pObj A reference to the parent (calling) object
+     * @param \object $pObj A reference to the parent (calling) object
      * @throws \RuntimeException
      * @see \TYPO3\CMS\Backend\Module\BaseScriptClass::checkExtObj()
      */
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedMethodsInTaskModuleController.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedMethodsInTaskModuleController.rst
deleted file mode 100644 (file)
index a3af460..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-.. include:: ../../Includes.txt
-
-===============================================================
-Deprecation: #86182 - Protected methods in TaskModuleController
-===============================================================
-
-See :issue:`86182`
-
-Description
-===========
-
-The following methods of class :php:`TYPO3\CMS\Taskcenter\Controller\TaskModuleController`
-changed their visibility from public to protected and should not be called any longer:
-
-* [not scanned] :php:`main()`
-* :php:`urlInIframe()` Will be removed in TYPO3 v10
-
-Impact
-======
-
-Calling one of the above methods from an external object triggers a PHP :php:`E_USER_DEPRECATED` error.
-
-
-Affected Installations
-======================
-
-The :php:`main()` methods is usually called internally only, extensions should not be affected by this.
-Method :php:`urlInIframe()` has been used by extensions that delivered task center tasks that have
-not been updated for a long time, the method is deprecated and usage should be removed.
-
-
-Migration
-=========
-
-No migration possible.
-
-.. index:: Backend, PHP-API, PartiallyScanned, ext:taskcenter
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedTaskModuleController.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-86182-ProtectedTaskModuleController.rst
new file mode 100644 (file)
index 0000000..782d23d
--- /dev/null
@@ -0,0 +1,71 @@
+.. include:: ../../Includes.txt
+
+====================================================
+Deprecation: #86182 - Protected TaskModuleController
+====================================================
+
+See :issue:`86182`
+
+Description
+===========
+
+Class :php:`TYPO3\CMS\Taskcenter\Controller\TaskModuleController` no longer inherits
+:php:`TYPO3\CMS\Backend\Module\BaseScriptClass`.
+
+Single task classes should no longer expect to have an instance of the :php:`TaskModuleController`
+set as :php:`$GLOBALS['SOBE']`.
+
+The following properties of class :php:`TYPO3\CMS\Taskcenter\Controller\TaskModuleController`
+changed their visibility from public to protected and should not be called any longer:
+
+* [not scanned] :php:`MCONF`
+* [not scanned] :php:`id`
+* [not scanned] :php:`MOD_MENU`
+* [not scanned] :php:`modMenu_type`
+* [not scanned] :php:`modMenu_setDefaultList`
+* [not scanned] :php:`modMenu_dontValidateList`
+* [not scanned] :php:`content`
+* [not scanned] :php:`perms_clause`
+* [not scanned] :php:`CMD`
+* [not scanned] :php:`extClassConf`
+* [not scanned] :php:`extObj`
+
+The following properties of class :php:`TYPO3\CMS\Taskcenter\Controller\TaskModuleController`
+changed their visibility from public to protected and should not be called any longer:
+
+* [not scanned] :php:`menuConfig`
+* [not scanned] :php:`mergeExternalItems`
+* [not scanned] :php:`handleExternalFunctionValue`
+* [not scanned] :php:`getExternalItemConfig`
+* [not scanned] :php:`main`
+* :php:`urlInIframe`
+* [not scanned] :php:`extObjHeader`
+* [not scanned] :php:`checkSubExtObj`
+* [not scanned] :php:`checkExtObj`
+* [not scanned] :php:`extObjContent`
+* [not scanned] :php:`getExtObjContent`
+
+Impact
+======
+
+Calling one of the above methods from an external object triggers a PHP :php:`E_USER_DEPRECATED` error.
+
+
+Affected Installations
+======================
+
+Most methods and properties are used internally in the :php:`TaskModuleController` only. Some
+tasks however make access them. Instances with extensions delivering additional tasks for the
+taskcenter may be affected.
+
+
+Migration
+=========
+
+Single task should no longer rely on having an instance of :php:`TaskModuleController` set as
+:php:`$GLOBALS['SOBE'], an instance of the object in given as first constructor argument.
+
+Properties and methods that have been set to protected should be calculated internally instead.
+
+
+.. index:: Backend, PHP-API, PartiallyScanned, ext:taskcenter
\ No newline at end of file
index c5891bc..0c9945a 100644 (file)
@@ -3261,7 +3261,7 @@ return [
         'numberOfMandatoryArguments' => 1,
         'maximumNumberOfArguments' => 1,
         'restFiles' => [
-            'Deprecation-86182-ProtectedMethodsInTaskModuleController.rst',
+            'Deprecation-86182-ProtectedTaskModuleController.rst',
         ],
     ],
     'TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController->renderLinkAttributeFields' => [
index ea6f150..824c9ba 100644 (file)
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\HttpUtility;
 
@@ -758,7 +759,11 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
         $dbAnalysis->getFromDB();
         // collect the records
         foreach ($dbAnalysis->itemArray as $el) {
-            $path = BackendUtility::getRecordPath($el['id'], $this->taskObject->perms_clause, $this->getBackendUser()->uc['titleLen']);
+            $path = BackendUtility::getRecordPath(
+                $el['id'],
+                $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW),
+                $this->getBackendUser()->uc['titleLen']
+            );
             $record = BackendUtility::getRecord($el['table'], $dbAnalysis->results[$el['table']][$el['id']]);
             $title = BackendUtility::getRecordTitle($el['table'], $dbAnalysis->results[$el['table']][$el['id']]);
             $description = htmlspecialchars($this->getLanguageService()->sL($GLOBALS['TCA'][$el['table']]['ctrl']['title']));
@@ -807,7 +812,7 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
                 $fullsearch->noDownloadB = 1;
                 $type = $sql_query['qC']['search_query_makeQuery'];
                 if ($sql_query['qC']['labels_noprefix'] === 'on') {
-                    $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix'] = 'on';
+                    $this->taskObject->MOD_SETTINGS['labels_noprefix'] = 'on';
                 }
                 $sqlQuery = $sql_query['qSelect'];
                 $queryIsEmpty = false;
@@ -818,8 +823,8 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
                             ->executeQuery($sqlQuery)->fetchAll();
                         $fullsearch->formW = 48;
                         // Additional configuration
-                        $GLOBALS['SOBE']->MOD_SETTINGS['search_result_labels'] = 1;
-                        $GLOBALS['SOBE']->MOD_SETTINGS['queryFields'] = $sql_query['qC']['queryFields'];
+                        $this->taskObject->MOD_SETTINGS['search_result_labels'] = 1;
+                        $this->taskObject->MOD_SETTINGS['queryFields'] = $sql_query['qC']['queryFields'];
                         $cP = $fullsearch->getQueryResultCode($type, $dataRows, $sql_query['qC']['queryTable']);
                         $actionContent = $cP['content'];
                         // If the result is rendered as csv or xml, show a download link
@@ -899,7 +904,10 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
             return $content;
         }
         // Loading current page record and checking access:
-        $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->taskObject->perms_clause);
+        $this->pageinfo = BackendUtility::readPageAccess(
+            $this->id,
+            $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
+        );
         $access = is_array($this->pageinfo);
         // If there is access to the page, then render the list contents and set up the document template object:
         if ($access) {
@@ -908,7 +916,6 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
             $dblist->script = GeneralUtility::getIndpEnv('REQUEST_URI');
             $dblist->calcPerms = $this->getBackendUser()->calcPerms($this->pageinfo);
             $dblist->thumbs = $this->getBackendUser()->uc['thumbnailsByDefault'];
-            $dblist->returnUrl = $this->taskObject->returnUrl;
             $dblist->allFields = 1;
             $dblist->showClipboard = 0;
             $dblist->disableSingleTableView = 1;
@@ -919,7 +926,7 @@ class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
             $dblist->dontShowClipControlPanels = (!$this->taskObject->MOD_SETTINGS['bigControlPanel'] && $dblist->clipObj->current === 'normal' && !$this->modTSconfig['properties']['showClipControlPanelsDespiteOfCMlayers']);
             // Initialize the listing object, dblist, for rendering the list:
             $this->pointer = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange(GeneralUtility::_GP('pointer'), 0, 100000);
-            $dblist->start($this->id, $this->table, $this->pointer, $this->taskObject->search_field, $this->taskObject->search_levels, $this->taskObject->showLimit);
+            $dblist->start($this->id, $this->table, $this->pointer);
             $dblist->setDispFields();
             // Render the list of tables:
             $dblist->generateList();
index c99ba17..c0533fb 100644 (file)
@@ -16,33 +16,179 @@ namespace TYPO3\CMS\Taskcenter\Controller;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Backend\Module\BaseScriptClass;
 use TYPO3\CMS\Backend\Template\ModuleTemplate;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
+use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
 use TYPO3\CMS\Core\Http\HtmlResponse;
+use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
+use TYPO3\CMS\Core\Messaging\FlashMessageService;
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Fluid\View\StandaloneView;
 use TYPO3\CMS\Taskcenter\TaskInterface;
 
 /**
- * This class provides a taskcenter for BE users
+ * This class provides a task center for BE users
  */
-class TaskModuleController extends BaseScriptClass
+class TaskModuleController
 {
+    use PublicPropertyDeprecationTrait;
     use PublicMethodDeprecationTrait;
 
     /**
      * @var array
      */
+    private $deprecatedPublicProperties = [
+        'MCONF' => 'Using TaskModuleController::$MCONF is deprecated and will not be possible anymore in TYPO3 v10.',
+        'id' => 'Using TaskModuleController::$id is deprecated and will not be possible anymore in TYPO3 v10.',
+        'MOD_MENU' => 'Using TaskModuleController::$MOD_MENU is deprecated and will not be possible anymore in TYPO3 v10.',
+        'modMenu_type' => 'Using TaskModuleController::$modMenu_type is deprecated and will not be possible anymore in TYPO3 v10.',
+        'modMenu_setDefaultList' => 'Using TaskModuleController::$$modMenu_setDefaultList is deprecated and will not be possible anymore in TYPO3 v10.',
+        'modMenu_dontValidateList' => 'Using TaskModuleController::$modMenu_dontValidateList is deprecated and will not be possible anymore in TYPO3 v10.',
+        'content' => 'Using TaskModuleController::$content is deprecated and will not be possible anymore in TYPO3 v10.',
+        'perms_clause' => 'Using TaskModuleController::$perms_clause is deprecated, the property will be removed in v10.',
+        'CMD' => 'Using TaskModuleController::$CMD is deprecated, the property will be removed in v10.',
+        'extClassConf' => 'Using TaskModuleController::$extClassConf is deprecated, the property will be removed in v10.',
+        'extObj' => 'Using TaskModuleController::$extObj is deprecated, the property will be removed in v10.',
+    ];
+
+    /**
+     * @var array
+     */
     private $deprecatedPublicMethods = [
+        'menuConfig' => 'Using TaskModuleController::menuConfig() is deprecated and will not be possible anymore in TYPO3 v10.',
+        'mergeExternalItems' => 'Using TaskModuleController::mergeExternalItems() is deprecated and will not be possible anymore in TYPO3 v10.',
+        'handleExternalFunctionValue' => 'Using TaskModuleController::handleExternalFunctionValue() is deprecated and will not be possible anymore in TYPO3 v10.',
+        'getExternalItemConfig' => 'Using TaskModuleController::getExternalItemConfig() is deprecated and will not be possible anymore in TYPO3 v10.',
         'main' => 'Using TaskModuleController::main() is deprecated and will not be possible anymore in TYPO3 v10.',
-        'urlInIframe' => 'Using SetupModuleController::urlInIframe() is deprecated. The method will be removed in TYPO3 v10.',
+        'urlInIframe' => 'Using TaskModuleController::urlInIframe() is deprecated. The method will be removed in TYPO3 v10.',
+        'extObjHeader' => 'Using TaskModuleController::extObjHeader() is deprecated. The method will be removed in TYPO3 v10.',
+        'checkSubExtObj' => 'Using TaskModuleController::checkSubExtObj() is deprecated. The method will be removed in TYPO3 v10.',
+        'checkExtObj' => 'Using TaskModuleController::checkExtObj() is deprecated. The method will be removed in TYPO3 v10.',
+        'extObjContent' => 'Using TaskModuleController::extObjContent() is deprecated. The method will be removed in TYPO3 v10.',
+        'getExtObjContent' => 'Using TaskModuleController::getExtObjContent() is deprecated. The method will be removed in TYPO3 v10.',
     ];
 
     /**
+     * Loaded with the global array $MCONF which holds some module configuration from the conf.php file of backend modules.
+     *
+     * @see init()
+     * @var array
+     */
+    protected $MCONF = [];
+
+    /**
+     * The integer value of the GET/POST var, 'id'. Used for submodules to the 'Web' module (page id)
+     *
+     * @see init()
+     * @var int
+     */
+    protected $id;
+
+    /**
+     * The value of GET/POST var, 'CMD'
+     *
+     * @see init()
+     * @var mixed
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected $CMD;
+
+    /**
+     * A WHERE clause for selection records from the pages table based on read-permissions of the current backend user.
+     *
+     * @see init()
+     * @var string
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected $perms_clause;
+
+    /**
+     * The module menu items array. Each key represents a key for which values can range between the items in the array of that key.
+     *
+     * @see init()
+     * @var array
+     */
+    protected $MOD_MENU = [
+        'function' => []
+    ];
+
+    /**
+     * Current settings for the keys of the MOD_MENU array
+     * Public since task objects use this.
+     *
+     * @see $MOD_MENU
+     * @var array
+     */
+    public $MOD_SETTINGS = [];
+
+    /**
+     * Module TSconfig based on PAGE TSconfig / USER TSconfig
+     * Public since task objects use this.
+     *
+     * @see menuConfig()
+     * @var array
+     */
+    public $modTSconfig;
+
+    /**
+     * If type is 'ses' then the data is stored as session-lasting data. This means that it'll be wiped out the next time the user logs in.
+     * Can be set from extension classes of this class before the init() function is called.
+     *
+     * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
+     * @var string
+     */
+    protected $modMenu_type = '';
+
+    /**
+     * dontValidateList can be used to list variables that should not be checked if their value is found in the MOD_MENU array. Used for dynamically generated menus.
+     * Can be set from extension classes of this class before the init() function is called.
+     *
+     * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
+     * @var string
+     */
+    protected $modMenu_dontValidateList = '';
+
+    /**
+     * List of default values from $MOD_MENU to set in the output array (only if the values from MOD_MENU are not arrays)
+     * Can be set from extension classes of this class before the init() function is called.
+     *
+     * @see menuConfig(), \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()
+     * @var string
+     */
+    protected $modMenu_setDefaultList = '';
+
+    /**
+     * Contains module configuration parts from TBE_MODULES_EXT if found
+     *
+     * @see handleExternalFunctionValue()
+     * @var array
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected $extClassConf;
+
+    /**
+     * Generally used for accumulating the output content of backend modules
+     *
+     * @var string
+     */
+    protected $content = '';
+
+    /**
+     * May contain an instance of a 'Function menu module' which connects to this backend module.
+     *
+     * @see checkExtObj()
+     * @var \object
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected $extObj;
+
+    /**
      * @var array
      */
     protected $pageinfo;
@@ -71,17 +217,28 @@ class TaskModuleController extends BaseScriptClass
         $this->MCONF = [
             'name' => $this->moduleName
         ];
-        parent::init();
+        // Name might be set from outside
+        if (!$this->MCONF['name']) {
+            $this->MCONF = $GLOBALS['MCONF'];
+        }
+        $this->id = (int)GeneralUtility::_GP('id');
+        // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+        $this->CMD = GeneralUtility::_GP('CMD');
+        // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+        $this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
+        $this->menuConfig();
+        $this->handleExternalFunctionValue();
     }
 
     /**
      * Adds items to the ->MOD_MENU array. Used for the function menu selector.
      */
-    public function menuConfig()
+    protected function menuConfig()
     {
         $this->MOD_MENU = ['mode' => []];
-        $this->MOD_MENU['mode']['information'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_overview');
-        $this->MOD_MENU['mode']['tasks'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_tasks');
+        $languageService = $this->getLanguageService();
+        $this->MOD_MENU['mode']['information'] = $languageService->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_overview');
+        $this->MOD_MENU['mode']['tasks'] = $languageService->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_tasks');
         // Copied from parent::menuConfig, because parent is hardcoded to menu.function,
         // however menu.function is already used for the individual tasks. Therefore we use menu.mode here.
         // Page/be_user TSconfig settings and blinding of menu-items
@@ -93,7 +250,17 @@ class TaskModuleController extends BaseScriptClass
                 unset($this->MOD_MENU['mode'][$key]);
             }
         }
-        parent::menuConfig();
+        // Page / user TSconfig settings and blinding of menu-items
+        // Now overwrite the stuff again for unknown reasons
+        $this->modTSconfig['properties'] = BackendUtility::getPagesTSconfig($this->id)['mod.'][$this->MCONF['name'] . '.'] ?? [];
+        $this->MOD_MENU['function'] = $this->mergeExternalItems($this->MCONF['name'], 'function', $this->MOD_MENU['function']);
+        $blindActions = $this->modTSconfig['properties']['menu.']['function.'] ?? [];
+        foreach ($blindActions as $key => $value) {
+            if (!$value && array_key_exists($key, $this->MOD_MENU['function'])) {
+                unset($this->MOD_MENU['function'][$key]);
+            }
+        }
+        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name'], $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
     }
 
     /**
@@ -139,9 +306,10 @@ class TaskModuleController extends BaseScriptClass
      */
     public function mainAction(ServerRequestInterface $request): ResponseInterface
     {
+        // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
         $GLOBALS['SOBE'] = $this;
-        $this->main();
 
+        $this->main();
         $this->moduleTemplate->setContent($this->content);
         return new HtmlResponse($this->moduleTemplate->renderContent());
     }
@@ -175,6 +343,7 @@ class TaskModuleController extends BaseScriptClass
      */
     protected function renderModuleContent()
     {
+        $languageService = $this->getLanguageService();
         $chosenTask = (string)$this->MOD_SETTINGS['function'];
         // Render the taskcenter task as default
         if (empty($chosenTask) || $chosenTask === 'index') {
@@ -193,8 +362,8 @@ class TaskModuleController extends BaseScriptClass
                 } else {
                     $flashMessage = GeneralUtility::makeInstance(
                         FlashMessage::class,
-                        $this->getLanguageService()->getLL('error-access'),
-                        $this->getLanguageService()->getLL('error_header'),
+                        $languageService->getLL('error-access'),
+                        $languageService->getLL('error_header'),
                         FlashMessage::ERROR
                     );
                 }
@@ -202,16 +371,16 @@ class TaskModuleController extends BaseScriptClass
                 // Error if the task is not an instance of \TYPO3\CMS\Taskcenter\TaskInterface
                 $flashMessage = GeneralUtility::makeInstance(
                     FlashMessage::class,
-                    sprintf($this->getLanguageService()->getLL('error_no-instance'), $taskClass, TaskInterface::class),
-                    $this->getLanguageService()->getLL('error_header'),
+                    sprintf($languageService->getLL('error_no-instance'), $taskClass, TaskInterface::class),
+                    $languageService->getLL('error_header'),
                     FlashMessage::ERROR
                 );
             }
         } else {
             $flashMessage = GeneralUtility::makeInstance(
                 FlashMessage::class,
-                $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tabdescr'),
-                $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
+                $languageService->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tabdescr'),
+                $languageService->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
                 FlashMessage::INFO
             );
         }
@@ -262,6 +431,7 @@ class TaskModuleController extends BaseScriptClass
 
     /**
      * Render the headline of a task including a title and an optional description.
+     * Public since task objects use this.
      *
      * @param string $title Title
      * @param string $description Description
@@ -279,15 +449,14 @@ class TaskModuleController extends BaseScriptClass
     }
 
     /**
-     * Render a list of items as a nicely formatted definition list including a
-     * link, icon, title and description.
+     * Render a list of items as a nicely formatted definition list including a link, icon, title and description.
      * The keys of a single item are:
      * - title:             Title of the item
      * - link:              Link to the task
      * - icon:              Path to the icon or Icon as HTML if it begins with <img
      * - description:       Description of the task, using htmlspecialchars()
-     * - descriptionHtml:   Description allowing HTML tags which will override the
-     * description
+     * - descriptionHtml:   Description allowing HTML tags which will override the description
+     * Public since task objects use this.
      *
      * @param array $items List of items to be displayed in the definition list.
      * @param bool $mainMenu Set it to TRUE to render the main menu
@@ -360,6 +529,7 @@ class TaskModuleController extends BaseScriptClass
      */
     protected function indexAction()
     {
+        $languageService = $this->getLanguageService();
         $content = '';
         $tasks = [];
         $defaultIcon = 'EXT:taskcenter/Resources/Public/Icons/module-taskcenter.svg';
@@ -373,7 +543,7 @@ class TaskModuleController extends BaseScriptClass
                         continue;
                     }
                     $link = (string)$uriBuilder->buildUriFromRoute('user_task') . '&SET[function]=' . $extKey . '.' . $taskClass;
-                    $taskTitle = $this->getLanguageService()->sL($task['title']);
+                    $taskTitle = $languageService->sL($task['title']);
                     $taskDescriptionHtml = '';
 
                     if (class_exists($taskClass)) {
@@ -387,7 +557,7 @@ class TaskModuleController extends BaseScriptClass
                     $tasks[$uniqueKey] = [
                         'title' => $taskTitle,
                         'descriptionHtml' => $taskDescriptionHtml,
-                        'description' => $this->getLanguageService()->sL($task['description']),
+                        'description' => $languageService->sL($task['description']),
                         'icon' => !empty($task['icon']) ? $task['icon'] : $defaultIcon,
                         'link' => $link,
                         'uid' => $extKey . '.' . $taskClass
@@ -398,7 +568,7 @@ class TaskModuleController extends BaseScriptClass
         } else {
             $flashMessage = GeneralUtility::makeInstance(
                 FlashMessage::class,
-                $this->getLanguageService()->getLL('no-tasks'),
+                $languageService->getLL('no-tasks'),
                 '',
                 FlashMessage::INFO
             );
@@ -485,9 +655,9 @@ class TaskModuleController extends BaseScriptClass
     /**
      * Returns the current BE user.
      *
-     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
+     * @return BackendUserAuthentication
      */
-    protected function getBackendUser()
+    protected function getBackendUser(): BackendUserAuthentication
     {
         return $GLOBALS['BE_USER'];
     }
@@ -495,18 +665,171 @@ class TaskModuleController extends BaseScriptClass
     /**
      * Returns LanguageService
      *
-     * @return \TYPO3\CMS\Core\Localization\LanguageService
+     * @return LanguageService
      */
-    protected function getLanguageService()
+    protected function getLanguageService(): LanguageService
     {
         return $GLOBALS['LANG'];
     }
 
     /**
+     * Public since task objects use this.
+     *
      * @return ModuleTemplate
      */
-    public function getModuleTemplate()
+    public function getModuleTemplate(): ModuleTemplate
     {
         return $this->moduleTemplate;
     }
+
+    /**
+     * Merges menu items from global array $TBE_MODULES_EXT
+     *
+     * @param string $modName Module name for which to find value
+     * @param string $menuKey Menu key, eg. 'function' for the function menu.
+     * @param array $menuArr The part of a MOD_MENU array to work on.
+     * @return array Modified array part.
+     * @access private
+     * @see \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), menuConfig()
+     */
+    protected function mergeExternalItems($modName, $menuKey, $menuArr)
+    {
+        $mergeArray = $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
+        if (is_array($mergeArray)) {
+            foreach ($mergeArray as $k => $v) {
+                if (((string)$v['ws'] === '' || $this->getBackendUser()->workspace === 0 && GeneralUtility::inList($v['ws'], 'online')) || $this->getBackendUser()->workspace === -1 && GeneralUtility::inList($v['ws'], 'offline') || $this->getBackendUser()->workspace > 0 && GeneralUtility::inList($v['ws'], 'custom')) {
+                    $menuArr[$k] = $this->getLanguageService()->sL($v['title']);
+                }
+            }
+        }
+        return $menuArr;
+    }
+
+    /**
+     * Loads $this->extClassConf with the configuration for the CURRENT function of the menu.
+     *
+     * @param string $MM_key The key to MOD_MENU for which to fetch configuration. 'function' is default since it is first and foremost used to get information per "extension object" (I think that is what its called)
+     * @param string $MS_value The value-key to fetch from the config array. If NULL (default) MOD_SETTINGS[$MM_key] will be used. This is useful if you want to force another function than the one defined in MOD_SETTINGS[function]. Call this in init() function of your Script Class: handleExternalFunctionValue('function', $forcedSubModKey)
+     * @see getExternalItemConfig(), init()
+     */
+    protected function handleExternalFunctionValue($MM_key = 'function', $MS_value = null)
+    {
+        if ($MS_value === null) {
+            $MS_value = $this->MOD_SETTINGS[$MM_key];
+        }
+        $this->extClassConf = $this->getExternalItemConfig($this->MCONF['name'], $MM_key, $MS_value);
+    }
+
+    /**
+     * Returns configuration values from the global variable $TBE_MODULES_EXT for the module given.
+     * For example if the module is named "web_info" and the "function" key ($menuKey) of MOD_SETTINGS is "stat" ($value) then you will have the values of $TBE_MODULES_EXT['webinfo']['MOD_MENU']['function']['stat'] returned.
+     *
+     * @param string $modName Module name
+     * @param string $menuKey Menu key, eg. "function" for the function menu. See $this->MOD_MENU
+     * @param string $value Optionally the value-key to fetch from the array that would otherwise have been returned if this value was not set. Look source...
+     * @return mixed The value from the TBE_MODULES_EXT array.
+     * @see handleExternalFunctionValue()
+     */
+    protected function getExternalItemConfig($modName, $menuKey, $value = '')
+    {
+        if (isset($GLOBALS['TBE_MODULES_EXT'][$modName])) {
+            return (string)$value !== '' ? $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey][$value] : $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
+        }
+        return null;
+    }
+
+    /**
+     * Creates an instance of the class found in $this->extClassConf['name'] in $this->extObj if any (this should hold three keys, "name", "path" and "title" if a "Function menu module" tries to connect...)
+     * This value in extClassConf might be set by an extension (in an ext_tables/ext_localconf file) which thus "connects" to a module.
+     * The array $this->extClassConf is set in handleExternalFunctionValue() based on the value of MOD_SETTINGS[function]
+     * If an instance is created it is initiated with $this passed as value and $this->extClassConf as second argument. Further the $this->MOD_SETTING is cleaned up again after calling the init function.
+     *
+     * @see handleExternalFunctionValue(), \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), $extObj
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function checkExtObj()
+    {
+        if (is_array($this->extClassConf) && $this->extClassConf['name']) {
+            $this->extObj = GeneralUtility::makeInstance($this->extClassConf['name']);
+            $this->extObj->init($this, $this->extClassConf);
+            // Re-write:
+            $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name'], $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
+        }
+    }
+
+    /**
+     * Calls the checkExtObj function in sub module if present.
+     *
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function checkSubExtObj()
+    {
+        if (is_object($this->extObj)) {
+            $this->extObj->checkExtObj();
+        }
+    }
+
+    /**
+     * Calls the 'header' function inside the "Function menu module" if present.
+     * A header function might be needed to add JavaScript or other stuff in the head. This can't be done in the main function because the head is already written.
+     *
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function extObjHeader()
+    {
+        if (is_callable([$this->extObj, 'head'])) {
+            $this->extObj->head();
+        }
+    }
+
+    /**
+     * Calls the 'main' function inside the "Function menu module" if present
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function extObjContent()
+    {
+        if ($this->extObj === null) {
+            $flashMessage = GeneralUtility::makeInstance(
+                FlashMessage::class,
+                $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:no_modules_registered'),
+                $this->getLanguageService()->getLL('title'),
+                FlashMessage::ERROR
+            );
+            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
+            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
+            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
+            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
+            $defaultFlashMessageQueue->enqueue($flashMessage);
+        } else {
+            $this->extObj->pObj = $this;
+            if (is_callable([$this->extObj, 'main'])) {
+                $this->content .= $this->extObj->main();
+            }
+        }
+    }
+
+    /**
+     * Return the content of the 'main' function inside the "Function menu module" if present
+     *
+     * @return string
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function getExtObjContent()
+    {
+        $savedContent = $this->content;
+        $this->content = '';
+        $this->extObjContent();
+        $newContent = $this->content;
+        $this->content = $savedContent;
+        return $newContent;
+    }
+
+    /**
+     * @return PageRenderer
+     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.
+     */
+    protected function getPageRenderer(): PageRenderer
+    {
+        return GeneralUtility::makeInstance(PageRenderer::class);
+    }
 }