From 3b39a1a94fca3af7019fedb2a3a5c139d4ceee0f Mon Sep 17 00:00:00 2001
From: Susanne Moog <susanne.moog@typo3.org>
Date: Fri, 2 Mar 2018 10:21:17 +0100
Subject: [PATCH] [TASK] Refactor AdminPanelView - remove module specifics

This is the second step in the AdminPanelView refactoring. It
extracts module specific code and uses a more generic API to
initialize, configure and display admin panel modules.

Resolves: #84118
Releases: master
Change-Id: I85a1e11dfd7d9397fabbfbd7d5cf658387056644
Reviewed-on: https://review.typo3.org/55985
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
---
 ...ublicMethodsOfAdminPanelViewDeprecated.rst |  42 ++
 .../Classes/AdminPanel/AbstractModule.php     | 107 +++-
 .../AdminPanel/AdminPanelModuleInterface.php  |  63 +-
 .../Classes/AdminPanel/CacheModule.php        |  44 ++
 .../Classes/AdminPanel/EditModule.php         |  82 ++-
 .../Classes/AdminPanel/InfoModule.php         |   2 +-
 .../Classes/AdminPanel/PreviewModule.php      | 107 +++-
 .../Classes/AdminPanel/TsDebugModule.php      |  30 +-
 .../TypoScriptFrontendController.php          |  35 +-
 .../Classes/Middleware/PageResolver.php       |   2 -
 .../frontend/Classes/View/AdminPanelView.php  | 559 ++++++++++--------
 .../Tests/Unit/View/AdminPanelViewTest.php    |  79 +--
 .../AdminPanelDisabledModuleFixture.php       | 131 ++++
 ...edShownOnSubmitInitializeModuleFixture.php | 132 +++++
 .../View/AdminPanelViewTest.php               |  99 ----
 .../Php/MethodCallMatcher.php                 |  63 ++
 16 files changed, 1106 insertions(+), 471 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst
 create mode 100644 typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelDisabledModuleFixture.php
 create mode 100644 typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelEnabledShownOnSubmitInitializeModuleFixture.php
 delete mode 100644 typo3/sysext/frontend/Tests/UnitDeprecated/View/AdminPanelViewTest.php

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst
new file mode 100644
index 000000000000..2aad46a61eeb
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst
@@ -0,0 +1,42 @@
+.. include:: ../../Includes.txt
+
+=========================================================================
+Deprecation: #84118 - Various public methods of AdminPanelView deprecated
+=========================================================================
+
+See :issue:`84118`
+
+Description
+===========
+
+To clean up the admin panel and provide a new API various functions of the main class `AdminPanelView` were deprecated:
+
+* `getAdminPanelHeaderData`
+* `isAdminModuleEnabled`
+* `saveConfigOptions`
+* `extGetFeAdminValue`
+* `forcePreview`
+* `isAdminModuleOpen`
+* `extGetHead`
+* `linkSectionHeader`
+* `extGetItem`
+
+
+Impact
+======
+
+Calling any of the mentioned methods triggers an `E_USER_DEPRECATED` PHP error.
+
+
+Affected Installations
+======================
+
+Any installation that calls one of the above methods.
+
+
+Migration
+=========
+
+Implement your own AdminPanel module by using the new API (see `AdminPanelModuleInterface`).
+
+.. index:: Frontend, FullyScanned, ext:frontend
\ No newline at end of file
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/AbstractModule.php b/typo3/sysext/frontend/Classes/AdminPanel/AbstractModule.php
index 43d90719b9b7..5987fc27813a 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/AbstractModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/AbstractModule.php
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Localization\LanguageService;
  */
 abstract class AbstractModule implements AdminPanelModuleInterface
 {
+
     /**
      * @inheritdoc
      */
@@ -34,6 +35,64 @@ abstract class AbstractModule implements AdminPanelModuleInterface
         return '';
     }
 
+    /**
+     * @inheritdoc
+     */
+    public function initializeModule(): void
+    {
+    }
+
+    /**
+     * Returns true if the module is
+     * -> either enabled via tsconfig admPanel.enable
+     * -> or any setting is overridden
+     * override is a way to use functionality of the admin panel without displaying the admin panel to users
+     * for example: hidden records or pages can be displayed by default
+     *
+     * @return bool
+     */
+    public function isEnabled(): bool
+    {
+        $identifier = $this->getIdentifier();
+        $result = $this->isEnabledViaTsConfig();
+        if ($this->getBackendUser()->extAdminConfig['override.'][$identifier] ?? false) {
+            $result = (bool)$this->getBackendUser()->extAdminConfig['override.'][$identifier];
+        }
+        return $result;
+    }
+
+    /**
+     * Uses the backend user session to determine if the module is open
+     *
+     * @return bool
+     */
+    public function isOpen(): bool
+    {
+        $option = 'display_' . $this->getIdentifier();
+        return isset($this->getBackendUser()->uc['TSFE_adminConfig'][$option])
+            ? (bool)$this->getBackendUser()->uc['TSFE_adminConfig'][$option]
+            : false;
+    }
+
+    /**
+     * Determines if the panel for this module is shown
+     * -> returns true if panel is enabled in TSConfig
+     *
+     * @see isEnabled()
+     * @return bool
+     */
+    public function isShown(): bool
+    {
+        return $this->isEnabledViaTsConfig();
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function onSubmit(array $input): void
+    {
+    }
+
     /**
      * @inheritdoc
      */
@@ -46,7 +105,7 @@ abstract class AbstractModule implements AdminPanelModuleInterface
      * Translate given key
      *
      * @param string $key Key for a label in the $LOCAL_LANG array of "sysext/lang/Resources/Private/Language/locallang_tsfe.xlf
-     * @param bool $convertWithHtmlpecialchars If TRUE the language-label will be sent through htmlspecialchars
+     * @param bool $convertWithHtmlspecialchars If TRUE the language-label will be sent through htmlspecialchars
      * @return string The value for the $key
      */
     protected function extGetLL($key, $convertWithHtmlspecialchars = true): string
@@ -58,6 +117,37 @@ abstract class AbstractModule implements AdminPanelModuleInterface
         return $labelStr;
     }
 
+    /**
+     * Returns the current BE user.
+     *
+     * @return \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
+     */
+    protected function getBackendUser(): FrontendBackendUserAuthentication
+    {
+        return $GLOBALS['BE_USER'];
+    }
+
+    /**
+     * Helper method to return configuration options
+     * Checks User TSConfig overrides and current backend user session
+     *
+     * @param string $option
+     * @return string
+     */
+    protected function getConfigurationOption(string $option): string
+    {
+        $beUser = $this->getBackendUser();
+        $identifier = $this->getIdentifier();
+
+        if ($option && isset($beUser->extAdminConfig['override.'][$identifier . '.'][$option])) {
+            $returnValue = $beUser->extAdminConfig['override.'][$identifier . '.'][$option];
+        } else {
+            $returnValue = $beUser->uc['TSFE_adminConfig'][$identifier . '_' . $option] ?? '';
+        }
+
+        return (string)$returnValue;
+    }
+
     /**
      * Returns LanguageService
      *
@@ -69,12 +159,19 @@ abstract class AbstractModule implements AdminPanelModuleInterface
     }
 
     /**
-     * Returns the current BE user.
+     * Returns true if TSConfig admPanel.enable is set for this module (or all modules)
      *
-     * @return \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
+     * @return bool
      */
-    protected function getBackendUser(): FrontendBackendUserAuthentication
+    protected function isEnabledViaTsConfig(): bool
     {
-        return $GLOBALS['BE_USER'];
+        $result = false;
+        $identifier = $this->getIdentifier();
+        if (!empty($this->getBackendUser()->extAdminConfig['enable.']['all'])) {
+            $result = true;
+        } elseif (!empty($this->getBackendUser()->extAdminConfig['enable.'][$identifier])) {
+            $result = true;
+        }
+        return $result;
     }
 }
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/AdminPanelModuleInterface.php b/typo3/sysext/frontend/Classes/AdminPanel/AdminPanelModuleInterface.php
index a6d2b2fe95c4..886b3c742123 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/AdminPanelModuleInterface.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/AdminPanelModuleInterface.php
@@ -23,6 +23,22 @@ namespace TYPO3\CMS\Frontend\AdminPanel;
  */
 interface AdminPanelModuleInterface
 {
+    /**
+     * Additional JavaScript code for this module
+     * (you should only use vanilla JS here, as you cannot
+     * rely on the web site providing a specific framework)
+     *
+     * @return string
+     */
+    public function getAdditionalJavaScriptCode(): string;
+
+    /**
+     * Module content as rendered HTML
+     *
+     * @return string
+     */
+    public function getContent(): string;
+
     /**
      * Identifier for this module,
      * for example "preview" or "cache"
@@ -39,25 +55,52 @@ interface AdminPanelModuleInterface
     public function getLabel(): string;
 
     /**
-     * Module content as rendered HTML
+     * Initialize the module - runs early in a TYPO3 request
+     */
+    public function initializeModule(): void;
+
+    /**
+     * Module is enabled
+     * -> should be initialized
+     * A module may be enabled but not shown
+     * -> only the initializeModule() method
+     * will be called
      *
-     * @return string
+     * @return bool
      */
-    public function getContent(): string;
+    public function isEnabled(): bool;
 
     /**
-     * Does this module need a form submit?
+     * Module is open
+     * -> module is enabled
+     * -> module panel is shown and open
      *
      * @return bool
      */
-    public function showFormSubmitButton(): bool;
+    public function isOpen(): bool;
 
     /**
-     * Additional JavaScript code for this module
-     * (you should only use vanilla JS here, as you cannot
-     * rely on the web site providing a specific framework)
+     * Module is shown
+     * -> module is enabled
+     * -> module panel should be displayed
      *
-     * @return string
+     * @return bool
      */
-    public function getAdditionalJavaScriptCode(): string;
+    public function isShown(): bool;
+
+    /**
+     * Executed on saving / submit of the configuration form
+     * Can be used to react to changed settings
+     * (for example: clearing a specific cache)
+     *
+     * @param array $input
+     */
+    public function onSubmit(array $input): void;
+
+    /**
+     * Does this module need a form submit?
+     *
+     * @return bool
+     */
+    public function showFormSubmitButton(): bool;
 }
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/CacheModule.php b/typo3/sysext/frontend/Classes/AdminPanel/CacheModule.php
index ac3786960d08..fd7a6dec83d1 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/CacheModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/CacheModule.php
@@ -16,8 +16,12 @@ namespace TYPO3\CMS\Frontend\AdminPanel;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+
 class CacheModule extends AbstractModule
 {
+
     /**
      * Creates the content for the "cache" section ("module") of the Admin Panel
      *
@@ -90,6 +94,38 @@ class CacheModule extends AbstractModule
         return $this->extGetLL('cache');
     }
 
+    /**
+     * @inheritdoc
+     */
+    public function initializeModule(): void
+    {
+        if ($this->getConfigurationOption('noCache')) {
+            $this->getTypoScriptFrontendController()->set_no_cache('Admin Panel: No Caching', true);
+        }
+    }
+
+    /**
+     * Clear cache on saving if requested
+     *
+     * @param array $input
+     */
+    public function onSubmit(array $input): void
+    {
+        if (($input['action']['clearCache'] ?? false) ||
+            isset($input['preview_showFluidDebug'])) {
+            $theStartId = (int)$input['cache_clearCacheId'];
+            $this->getTypoScriptFrontendController()
+                ->clearPageCacheContent_pidList(
+                    $this->getBackendUser()->extGetTreeList(
+                        $theStartId,
+                        (int)$this->getConfigurationOption('clearCacheLevels'),
+                        0,
+                        $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
+                    ) . $theStartId
+                );
+        }
+    }
+
     /**
      * @inheritdoc
      */
@@ -97,4 +133,12 @@ class CacheModule extends AbstractModule
     {
         return true;
     }
+
+    /**
+     * @return TypoScriptFrontendController
+     */
+    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
+    {
+        return $GLOBALS['TSFE'];
+    }
 }
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/EditModule.php b/typo3/sysext/frontend/Classes/AdminPanel/EditModule.php
index a8939a5d9d20..bb5e62b94d84 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/EditModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/EditModule.php
@@ -67,37 +67,35 @@ class EditModule extends AbstractModule
 
             $output[] = $this->getBackendUser()->adminPanel->ext_makeToolBar();
 
-            if (!GeneralUtility::_GP('ADMCMD_view')) {
-                $onClick = '
-                    if (parent.opener && parent.opener.top && parent.opener.top.TS) {
-                        parent.opener.top.fsMod.recentIds["web"]=' .
-                           (int)$this->getTypoScriptFrontendController()->page['uid'] .
-                           ';
-                        if (parent.opener.top && parent.opener.top.nav_frame && parent.opener.top.nav_frame.refresh_nav) {
-                            parent.opener.top.nav_frame.refresh_nav();
-                        }
-                        parent.opener.top.goToModule("' .
-                           $pageModule .
-                           '");
-                        parent.opener.top.focus();
-                    } else {
-                        vHWin=window.open(' .
-                           GeneralUtility::quoteJSvalue(BackendUtility::getBackendScript()) .
-                           ',\'' .
-                           md5('Typo3Backend-' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) .
-                           '\');
-                        vHWin.focus();
+            $onClick = '
+                if (parent.opener && parent.opener.top && parent.opener.top.TS) {
+                    parent.opener.top.fsMod.recentIds["web"]=' .
+                       (int)$this->getTypoScriptFrontendController()->page['uid'] .
+                       ';
+                    if (parent.opener.top && parent.opener.top.nav_frame && parent.opener.top.nav_frame.refresh_nav) {
+                        parent.opener.top.nav_frame.refresh_nav();
                     }
-                    return false;
-                ';
-                $output[] = '<div class="typo3-adminPanel-form-group">';
-                $output[] = '  <a class="typo3-adminPanel-btn typo3-adminPanel-btn-default" href="#" onclick="' .
-                            htmlspecialchars($onClick) .
-                            '">';
-                $output[] = '    ' . $this->extGetLL('edit_openAB');
-                $output[] = '  </a>';
-                $output[] = '</div>';
-            }
+                    parent.opener.top.goToModule("' .
+                       $pageModule .
+                       '");
+                    parent.opener.top.focus();
+                } else {
+                    vHWin=window.open(' .
+                       GeneralUtility::quoteJSvalue(BackendUtility::getBackendScript()) .
+                       ',\'' .
+                       md5('Typo3Backend-' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) .
+                       '\');
+                    vHWin.focus();
+                }
+                return false;
+            ';
+            $output[] = '<div class="typo3-adminPanel-form-group">';
+            $output[] = '  <a class="typo3-adminPanel-btn typo3-adminPanel-btn-default" href="#" onclick="' .
+                        htmlspecialchars($onClick) .
+                        '">';
+            $output[] = '    ' . $this->extGetLL('edit_openAB');
+            $output[] = '  </a>';
+            $output[] = '</div>';
         }
         return implode('', $output);
     }
@@ -118,6 +116,30 @@ class EditModule extends AbstractModule
         return $this->extGetLL('edit');
     }
 
+    /**
+     * Initialize the edit module
+     * Includes the frontend edit initialization
+     *
+     * @todo move into fe_edit (including the module)
+     */
+    public function initializeModule(): void
+    {
+        $extFeEditLoaded = ExtensionManagementUtility::isLoaded('feedit');
+        $typoScriptFrontend = $this->getTypoScriptFrontendController();
+        $typoScriptFrontend->displayEditIcons = $this->getConfigurationOption('displayIcons');
+        $typoScriptFrontend->displayFieldEditIcons = $this->getConfigurationOption('displayFieldIcons');
+
+        if (GeneralUtility::_GP('ADMCMD_editIcons')) {
+            $typoScriptFrontend->displayFieldEditIcons = '1';
+        }
+        if ($extFeEditLoaded && $typoScriptFrontend->displayEditIcons) {
+            $typoScriptFrontend->set_no_cache('Admin Panel: Display edit icons', true);
+        }
+        if ($extFeEditLoaded && $typoScriptFrontend->displayFieldEditIcons) {
+            $typoScriptFrontend->set_no_cache('Admin Panel: Display field edit icons', true);
+        }
+    }
+
     /**
      * @inheritdoc
      */
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/InfoModule.php b/typo3/sysext/frontend/Classes/AdminPanel/InfoModule.php
index 5fed7b4a455e..a74cc0d7c2ef 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/InfoModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/InfoModule.php
@@ -37,7 +37,7 @@ class InfoModule extends AbstractModule
         $tsfe = $this->getTypoScriptFrontendController();
         if ($this->getBackendUser()->uc['TSFE_adminConfig']['display_info']) {
             $tableArr = [];
-            if ($this->getBackendUser()->adminPanel->extGetFeAdminValue('cache', 'noCache')) {
+            if ($this->getConfigurationOption('noCache')) {
                 $theBytes = 0;
                 $count = 0;
                 if (!empty($tsfe->imagesOnPage)) {
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/PreviewModule.php b/typo3/sysext/frontend/Classes/AdminPanel/PreviewModule.php
index 66ef905b156a..a003366b9466 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/PreviewModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/PreviewModule.php
@@ -20,12 +20,28 @@ use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * Admin Panel Preview Module
  */
 class PreviewModule extends AbstractModule
 {
+    /**
+     * Force the preview panel to be opened
+     *
+     * @var bool
+     */
+    protected $forceOpen = false;
+
+    /**
+     * @inheritdoc
+     */
+    public function getAdditionalJavaScriptCode(): string
+    {
+        return 'TSFEtypo3FormFieldSet("TSFE_ADMIN_PANEL[preview_simulateDate]", "datetime", "", 0, 0);';
+    }
 
     /**
      * Creates the content for the "preview" section ("module") of the Admin Panel
@@ -147,6 +163,57 @@ class PreviewModule extends AbstractModule
         return $this->extGetLL('preview');
     }
 
+    public function initializeModule(): void
+    {
+        $this->initializeFrontendPreview();
+        if (GeneralUtility::_GP('ADMCMD_simUser')) {
+            $this->getBackendUser()->uc['TSFE_adminConfig']['preview_simulateUserGroup'] = (int)GeneralUtility::_GP(
+                'ADMCMD_simUser'
+            );
+            $this->forceOpen = true;
+        }
+        if (GeneralUtility::_GP('ADMCMD_simTime')) {
+            $this->getBackendUser()->uc['TSFE_adminConfig']['preview_simulateDate'] = (int)GeneralUtility::_GP(
+                'ADMCMD_simTime'
+            );
+            $this->forceOpen = true;
+        }
+    }
+
+    /**
+     * Force module to be shown if either time or users/groups are simulated
+     *
+     * @return bool
+     */
+    public function isShown(): bool
+    {
+        if ($this->forceOpen) {
+            return true;
+        }
+        return parent::isShown();
+    }
+
+    /**
+     * Clear page cache if fluid debug output is enabled
+     *
+     * @param array $input
+     */
+    public function onSubmit(array $input): void
+    {
+        if ($input['preview_showFluidDebug'] ?? false) {
+            $theStartId = (int)$this->getTypoScriptFrontendController()->id;
+            $this->getTypoScriptFrontendController()
+                ->clearPageCacheContent_pidList(
+                    $this->getBackendUser()->extGetTreeList(
+                        $theStartId,
+                        0,
+                        0,
+                        $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW)
+                    ) . $theStartId
+                );
+        }
+    }
+
     /**
      * @inheritdoc
      */
@@ -156,10 +223,44 @@ class PreviewModule extends AbstractModule
     }
 
     /**
-     * @inheritdoc
+     * @return TypoScriptFrontendController
      */
-    public function getAdditionalJavaScriptCode(): string
+    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
     {
-        return 'TSFEtypo3FormFieldSet("TSFE_ADMIN_PANEL[preview_simulateDate]", "datetime", "", 0, 0);';
+        return $GLOBALS['TSFE'];
+    }
+
+    /**
+     * Initialize frontend preview functionality incl.
+     * simulation of users or time
+     */
+    protected function initializeFrontendPreview()
+    {
+        $tsfe = $this->getTypoScriptFrontendController();
+        $tsfe->clear_preview();
+        $tsfe->fePreview = 1;
+        $tsfe->showHiddenPage = (bool)$this->getConfigurationOption('showHiddenPages');
+        $tsfe->showHiddenRecords = (bool)$this->getConfigurationOption('showHiddenRecords');
+        // Simulate date
+        $simTime = $this->getConfigurationOption('simulateDate');
+        if ($simTime) {
+            $GLOBALS['SIM_EXEC_TIME'] = $simTime;
+            $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
+        }
+        // simulate user
+        $tsfe->simUserGroup = $this->getConfigurationOption('simulateUserGroup');
+        if ($tsfe->simUserGroup) {
+            if ($tsfe->fe_user->user) {
+                $tsfe->fe_user->user[$tsfe->fe_user->usergroup_column] = $tsfe->simUserGroup;
+            } else {
+                $tsfe->fe_user = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
+                $tsfe->fe_user->user = [
+                    $tsfe->fe_user->usergroup_column => $tsfe->simUserGroup,
+                ];
+            }
+        }
+        if (!$tsfe->simUserGroup && !$simTime && !$tsfe->showHiddenPage && !$tsfe->showHiddenRecords) {
+            $tsfe->fePreview = 0;
+        }
     }
 }
diff --git a/typo3/sysext/frontend/Classes/AdminPanel/TsDebugModule.php b/typo3/sysext/frontend/Classes/AdminPanel/TsDebugModule.php
index b32a0db6f530..e51c181be11c 100644
--- a/typo3/sysext/frontend/Classes/AdminPanel/TsDebugModule.php
+++ b/typo3/sysext/frontend/Classes/AdminPanel/TsDebugModule.php
@@ -18,6 +18,7 @@ namespace TYPO3\CMS\Frontend\AdminPanel;
 
 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
 
 /**
  * Admin Panel TypoScript Debug Module
@@ -97,10 +98,10 @@ class TsDebugModule extends AbstractModule
             $output[] = '</div>';
 
             $timeTracker = $this->getTimeTracker();
-            $timeTracker->printConf['flag_tree'] = $this->getBackendUser()->adminPanel->extGetFeAdminValue('tsdebug', 'tree');
-            $timeTracker->printConf['allTime'] = $this->getBackendUser()->adminPanel->extGetFeAdminValue('tsdebug', 'displayTimes');
-            $timeTracker->printConf['flag_messages'] = $this->getBackendUser()->adminPanel->extGetFeAdminValue('tsdebug', 'displayMessages');
-            $timeTracker->printConf['flag_content'] = $this->getBackendUser()->adminPanel->extGetFeAdminValue('tsdebug', 'displayContent');
+            $timeTracker->printConf['flag_tree'] = $this->getConfigurationOption('tree');
+            $timeTracker->printConf['allTime'] = $this->getConfigurationOption('displayTimes');
+            $timeTracker->printConf['flag_messages'] = $this->getConfigurationOption('displayMessages');
+            $timeTracker->printConf['flag_content'] = $this->getConfigurationOption('displayContent');
             $output[] = $timeTracker->printTSlog();
         }
         return implode('', $output);
@@ -122,6 +123,19 @@ class TsDebugModule extends AbstractModule
         return $this->extGetLL('tsdebug');
     }
 
+    /**
+     * @inheritdoc
+     */
+    public function initializeModule(): void
+    {
+        $typoScriptFrontend = $this->getTypoScriptFrontendController();
+        $typoScriptFrontend->forceTemplateParsing = (bool)$this->getConfigurationOption('forceTemplateParsing');
+        if ($typoScriptFrontend->forceTemplateParsing) {
+            $typoScriptFrontend->set_no_cache('Admin Panel: Force template parsing', true);
+        }
+        $this->getTimeTracker()->LR = (bool)$this->getConfigurationOption('LR');
+    }
+
     /**
      * @inheritdoc
      */
@@ -137,4 +151,12 @@ class TsDebugModule extends AbstractModule
     {
         return GeneralUtility::makeInstance(TimeTracker::class);
     }
+
+    /**
+     * @return TypoScriptFrontendController
+     */
+    protected function getTypoScriptFrontendController(): TypoScriptFrontendController
+    {
+        return $GLOBALS['TSFE'];
+    }
 }
diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
index bcc95b0150ac..43740d845e08 100644
--- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
+++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php
@@ -55,7 +55,6 @@ use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 use TYPO3\CMS\Frontend\Http\UrlHandlerInterface;
 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
 use TYPO3\CMS\Frontend\Page\PageRepository;
-use TYPO3\CMS\Frontend\View\AdminPanelView;
 
 /**
  * Class for the built TypoScript based frontend. Instantiated in
@@ -1132,38 +1131,10 @@ class TypoScriptFrontendController implements LoggerAwareInterface
             return null;
         }
         $originalFrontendUser = null;
-        // Backend user preview features:
-        if ($backendUser->adminPanel instanceof AdminPanelView) {
-            $this->fePreview = (int)$backendUser->adminPanel->extGetFeAdminValue('preview');
-            // If admin panel preview is enabled...
-            if ($this->fePreview) {
-                if ($this->fe_user->user) {
-                    $originalFrontendUser = $this->fe_user->user[$this->fe_user->usergroup_column];
-                }
-                $this->showHiddenPage = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenPages');
-                $this->showHiddenRecords = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenRecords');
-                // Simulate date
-                $simTime = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateDate');
-                if ($simTime) {
-                    $GLOBALS['SIM_EXEC_TIME'] = $simTime;
-                    $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
-                }
-                // simulate user
-                $this->simUserGroup = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateUserGroup');
-                if ($this->simUserGroup) {
-                    if ($this->fe_user->user) {
-                        $this->fe_user->user[$this->fe_user->usergroup_column] = $this->simUserGroup;
-                    } else {
-                        $this->fe_user->user = [
-                            $this->fe_user->usergroup_column => $this->simUserGroup
-                        ];
-                    }
-                }
-                if (!$this->simUserGroup && !$simTime && !$this->showHiddenPage && !$this->showHiddenRecords) {
-                    $this->fePreview = 0;
-                }
-            }
+        if ($this->fe_user->user) {
+            $originalFrontendUser = $this->fe_user->user[$this->fe_user->usergroup_column];
         }
+
         // The preview flag is set if the current page turns out to be hidden
         if ($this->id && $this->determineIdIsHiddenPage()) {
             $this->fePreview = 1;
diff --git a/typo3/sysext/frontend/Classes/Middleware/PageResolver.php b/typo3/sysext/frontend/Classes/Middleware/PageResolver.php
index 1ed9c1c13b5e..4400771223fa 100644
--- a/typo3/sysext/frontend/Classes/Middleware/PageResolver.php
+++ b/typo3/sysext/frontend/Classes/Middleware/PageResolver.php
@@ -43,7 +43,6 @@ class PageResolver implements MiddlewareInterface
     {
         $GLOBALS['TSFE']->siteScript = $request->getAttribute('normalizedParams')->getSiteScript();
         $this->checkAlternativeIdMethods($GLOBALS['TSFE']);
-        $GLOBALS['TSFE']->clear_preview();
         $GLOBALS['TSFE']->determineId();
 
         // No access? Then remove user & Re-evaluate the page-id
@@ -51,7 +50,6 @@ class PageResolver implements MiddlewareInterface
             unset($GLOBALS['BE_USER']);
             $GLOBALS['TSFE']->beUserLogin = false;
             $this->checkAlternativeIdMethods($GLOBALS['TSFE']);
-            $GLOBALS['TSFE']->clear_preview();
             $GLOBALS['TSFE']->determineId();
         }
 
diff --git a/typo3/sysext/frontend/Classes/View/AdminPanelView.php b/typo3/sysext/frontend/Classes/View/AdminPanelView.php
index dda6cc723d9e..3815dd46c8a1 100644
--- a/typo3/sysext/frontend/Classes/View/AdminPanelView.php
+++ b/typo3/sysext/frontend/Classes/View/AdminPanelView.php
@@ -1,4 +1,5 @@
 <?php
+
 namespace TYPO3\CMS\Frontend\View;
 
 /*
@@ -21,9 +22,7 @@ use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Service\DependencyOrderingService;
-use TYPO3\CMS\Core\TimeTracker\TimeTracker;
 use TYPO3\CMS\Core\Type\Bitmask\Permission;
-use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Frontend\AdminPanel\AdminPanelModuleInterface;
@@ -57,13 +56,6 @@ class AdminPanelView
      */
     protected $iconFactory;
 
-    /**
-     * Determines whether EXT:feedit is loaded
-     *
-     * @var bool
-     */
-    protected $extFeEditLoaded = false;
-
     /**
      * Array of adminPanel modules
      *
@@ -84,43 +76,24 @@ class AdminPanelView
      */
     public function initialize()
     {
+        $this->validateSortAndInitializeModules();
         $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
-        $this->saveConfigOptions();
-        $typoScriptFrontend = $this->getTypoScriptFrontendController();
-        // Setting some values based on the admin panel
-        $this->extFeEditLoaded = ExtensionManagementUtility::isLoaded('feedit');
-        $this->validateSortAndInitiateModules();
-        $typoScriptFrontend->forceTemplateParsing = $this->extGetFeAdminValue('tsdebug', 'forceTemplateParsing');
-        $typoScriptFrontend->displayEditIcons = $this->extGetFeAdminValue('edit', 'displayIcons');
-        $typoScriptFrontend->displayFieldEditIcons = $this->extGetFeAdminValue('edit', 'displayFieldIcons');
-        if (GeneralUtility::_GP('ADMCMD_editIcons')) {
-            $typoScriptFrontend->displayFieldEditIcons = 1;
-        }
-        if (GeneralUtility::_GP('ADMCMD_simUser')) {
-            $this->getBackendUser()->uc['TSFE_adminConfig']['preview_simulateUserGroup'] = (int)GeneralUtility::_GP('ADMCMD_simUser');
-            $this->ext_forcePreview = true;
-        }
-        if (GeneralUtility::_GP('ADMCMD_simTime')) {
-            $this->getBackendUser()->uc['TSFE_adminConfig']['preview_simulateDate'] = (int)GeneralUtility::_GP('ADMCMD_simTime');
-            $this->ext_forcePreview = true;
-        }
-        if ($typoScriptFrontend->forceTemplateParsing) {
-            $typoScriptFrontend->set_no_cache('Admin Panel: Force template parsing', true);
-        } elseif ($this->extFeEditLoaded && $typoScriptFrontend->displayEditIcons) {
-            $typoScriptFrontend->set_no_cache('Admin Panel: Display edit icons', true);
-        } elseif ($this->extFeEditLoaded && $typoScriptFrontend->displayFieldEditIcons) {
-            $typoScriptFrontend->set_no_cache('Admin Panel: Display field edit icons', true);
-        } elseif (GeneralUtility::_GP('ADMCMD_view')) {
-            $typoScriptFrontend->set_no_cache('Admin Panel: Display preview', true);
+        $this->saveConfiguration();
+
+        foreach ($this->modules as $module) {
+            if ($module->isEnabled()) {
+                $module->initializeModule();
+            }
         }
     }
 
     /**
-     * Add an additional stylesheet
+     * Returns a link tag with the admin panel stylesheet
+     * defined using TBE_STYLES
      *
      * @return string
      */
-    public function getAdminPanelHeaderData()
+    protected function getAdminPanelStylesheet(): string
     {
         $result = '';
         if (!empty($GLOBALS['TBE_STYLES']['stylesheets']['admPanel'])) {
@@ -131,149 +104,25 @@ class AdminPanelView
     }
 
     /**
-     * Checks if an Admin Panel section ("module") is available for the user. If so, TRUE is returned.
-     *
-     * @param string $key The module key, eg. "edit", "preview", "info" etc.
-     * @return bool
-     */
-    public function isAdminModuleEnabled($key)
-    {
-        $result = false;
-        // Returns TRUE if the module checked is "preview" and the forcePreview flag is set.
-        if ($key === 'preview' && $this->ext_forcePreview) {
-            $result = true;
-        } elseif (!empty($this->getBackendUser()->extAdminConfig['enable.']['all'])) {
-            $result = true;
-        } elseif (!empty($this->getBackendUser()->extAdminConfig['enable.'][$key])) {
-            $result = true;
-        }
-        return $result;
-    }
-
-    /**
-     * Saves any change in settings made in the Admin Panel.
-     * Called from \TYPO3\CMS\Frontend\Http\RequestHandler right after access check for the Admin Panel
-     */
-    public function saveConfigOptions()
-    {
-        $input = GeneralUtility::_GP('TSFE_ADMIN_PANEL');
-        $beUser = $this->getBackendUser();
-        if (is_array($input)) {
-            // Setting
-            $beUser->uc['TSFE_adminConfig'] = array_merge(!is_array($beUser->uc['TSFE_adminConfig']) ? [] : $beUser->uc['TSFE_adminConfig'], $input);
-            unset($beUser->uc['TSFE_adminConfig']['action']);
-            // Actions:
-            if (($input['action']['clearCache'] && $this->isAdminModuleEnabled('cache')) || isset($input['preview_showFluidDebug'])) {
-                $theStartId = (int)$input['cache_clearCacheId'];
-                $this->getTypoScriptFrontendController()
-                    ->clearPageCacheContent_pidList(
-                        $beUser->extGetTreeList(
-                            $theStartId,
-                            $this->extGetFeAdminValue(
-                                'cache',
-                                'clearCacheLevels'
-                            ),
-                            0,
-                            $beUser->getPagePermsClause(Permission::PAGE_SHOW)
-                        ) . $theStartId
-                    );
-            }
-            // Saving
-            $beUser->writeUC();
-            // Flush fluid template cache
-            $cacheManager = new CacheManager();
-            $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
-            $cacheManager->getCache('fluid_template')->flush();
-        }
-        $this->getTimeTracker()->LR = $this->extGetFeAdminValue('tsdebug', 'LR');
-        if ($this->extGetFeAdminValue('cache', 'noCache')) {
-            $this->getTypoScriptFrontendController()->set_no_cache('Admin Panel: No Caching', true);
-        }
-    }
-
-    /**
-     * Returns the value for an Admin Panel setting.
-     *
-     * @param string $sectionName Module key
-     * @param string $val Setting key
-     * @return mixed The setting value
-     */
-    public function extGetFeAdminValue($sectionName, $val = '')
-    {
-        if (!$this->isAdminModuleEnabled($sectionName)) {
-            return null;
-        }
-
-        $beUser = $this->getBackendUser();
-        // Exceptions where the values can be overridden (forced) from backend:
-        // deprecated
-        if (
-            $sectionName === 'edit' && (
-                $val === 'displayIcons' && $beUser->extAdminConfig['module.']['edit.']['forceDisplayIcons'] ||
-                $val === 'displayFieldIcons' && $beUser->extAdminConfig['module.']['edit.']['forceDisplayFieldIcons'] ||
-                $val === 'editNoPopup' && $beUser->extAdminConfig['module.']['edit.']['forceNoPopup']
-            )
-        ) {
-            return true;
-        }
-
-        // Override all settings with user TSconfig
-        if ($val && isset($beUser->extAdminConfig['override.'][$sectionName . '.'][$val])) {
-            return $beUser->extAdminConfig['override.'][$sectionName . '.'][$val];
-        }
-        if (!$val && isset($beUser->extAdminConfig['override.'][$sectionName])) {
-            return $beUser->extAdminConfig['override.'][$sectionName];
-        }
-
-        $returnValue = $val ? $beUser->uc['TSFE_adminConfig'][$sectionName . '_' . $val] : 1;
-
-        // Exception for preview
-        if ($sectionName === 'preview' && $this->ext_forcePreview) {
-            return !$val ? true : $returnValue;
-        }
-
-        // See if the menu is expanded!
-        return $this->isAdminModuleOpen($sectionName) ? $returnValue : null;
-    }
-
-    /**
-     * Enables the force preview option.
-     */
-    public function forcePreview()
-    {
-        $this->ext_forcePreview = true;
-    }
-
-    /**
-     * Returns TRUE if admin panel module is open
-     *
-     * @param string $key Module key
-     * @return bool TRUE, if the admin panel is open for the specified admin panel module key.
-     */
-    public function isAdminModuleOpen($key)
-    {
-        return $this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] && $this->getBackendUser()->uc['TSFE_adminConfig']['display_' . $key];
-    }
-
-    /**
-     * @param string $key
-     * @param string $content
-     * @param string $label
+     * Render a single module with header panel
      *
+     * @param \TYPO3\CMS\Frontend\AdminPanel\AdminPanelModuleInterface $module
      * @return string
      */
-    protected function getModule($key, $content, $label = '')
+    protected function getModule(AdminPanelModuleInterface $module): string
     {
         $output = [];
 
-        if ($this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] && $this->isAdminModuleEnabled($key)) {
-            $output[] = '<div class="typo3-adminPanel-section typo3-adminPanel-section-' . ($this->isAdminModuleOpen($key) ? 'open' : 'closed') . '">';
+        if ($module->isEnabled()) {
+            $output[] = '<div class="typo3-adminPanel-section typo3-adminPanel-section-' .
+                        ($module->isOpen() ? 'open' : 'closed') .
+                        '">';
             $output[] = '  <div class="typo3-adminPanel-section-title">';
-            $output[] = '    ' . $this->linkSectionHeader($key, $label ?: $this->extGetLL($key));
+            $output[] = '    ' . $this->getSectionOpenerLink($module);
             $output[] = '  </div>';
-            if ($this->isAdminModuleOpen($key)) {
+            if ($module->isOpen()) {
                 $output[] = '<div class="typo3-adminPanel-section-body">';
-                $output[] = '  ' . $content;
+                $output[] = '  ' . $module->getContent();
                 $output[] = '</div>';
             }
             $output[] = '</div>';
@@ -294,30 +143,39 @@ class AdminPanelView
 
         $moduleContent = '';
 
-        foreach ($this->modules as $module) {
-            if ($this->isAdminModuleOpen($module->getIdentifier())) {
-                $this->extNeedUpdate = !$this->extNeedUpdate ? $module->showFormSubmitButton() : true;
-                $this->extJSCODE .= $module->getAdditionalJavaScriptCode();
+        if ($this->isAdminPanelActivated()) {
+            foreach ($this->modules as $module) {
+                if ($module->isOpen()) {
+                    $this->extNeedUpdate = !$this->extNeedUpdate ? $module->showFormSubmitButton() : true;
+                    $this->extJSCODE .= $module->getAdditionalJavaScriptCode();
+                }
+                $moduleContent .= $this->getModule($module);
             }
-            $moduleContent .= $this->getModule($module->getIdentifier(), $module->getContent(), $module->getLabel());
-        }
 
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_adminpanel.php']['extendAdminPanel'] ?? [] as $className) {
-            trigger_error(
-                'The hook $GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_adminpanel.php\'][\'extendAdminPanel\'] is deprecated, register an AdminPanelModule instead.',
-                E_USER_DEPRECATED
-            );
-            $hookObject = GeneralUtility::makeInstance($className);
-            if (!$hookObject instanceof AdminPanelViewHookInterface) {
-                throw new \UnexpectedValueException($className . ' must implement interface ' . AdminPanelViewHookInterface::class, 1311942539);
-            }
-            $content = $hookObject->extendAdminPanel($moduleContent, $this);
-            if ($content) {
-                $moduleContent .= '<div class="typo3-adminPanel-section typo3-adminPanel-section-open">';
-                $moduleContent .= '  <div class="typo3-adminPanel-section-body">';
-                $moduleContent .= '    ' . $content;
-                $moduleContent .= '  </div>';
-                $moduleContent .= '</div>';
+            foreach (
+                $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_adminpanel.php']['extendAdminPanel']
+                ??
+                [] as $className
+            ) {
+                trigger_error(
+                    'The hook $GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'tslib/class.tslib_adminpanel.php\'][\'extendAdminPanel\'] is deprecated, register an AdminPanelModule instead.',
+                    E_USER_DEPRECATED
+                );
+                $hookObject = GeneralUtility::makeInstance($className);
+                if (!$hookObject instanceof AdminPanelViewHookInterface) {
+                    throw new \UnexpectedValueException(
+                        $className . ' must implement interface ' . AdminPanelViewHookInterface::class,
+                        1311942539
+                    );
+                }
+                $content = $hookObject->extendAdminPanel($moduleContent, $this);
+                if ($content) {
+                    $moduleContent .= '<div class="typo3-adminPanel-section typo3-adminPanel-section-open">';
+                    $moduleContent .= '  <div class="typo3-adminPanel-section-body">';
+                    $moduleContent .= '    ' . $content;
+                    $moduleContent .= '  </div>';
+                    $moduleContent .= '</div>';
+                }
             }
         }
 
@@ -341,9 +199,13 @@ class AdminPanelView
             }
         }
         $output[] = '  <input type="hidden" name="TSFE_ADMIN_PANEL[display_top]" value="0" />';
-        $output[] = '  <input id="typo3AdminPanelEnable" type="checkbox" onchange="document.TSFE_ADMIN_PANEL_FORM.submit();" name="TSFE_ADMIN_PANEL[display_top]" value="1"' . ($this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] ? ' checked="checked"' : '') . '/>';
+        $output[] = '  <input id="typo3AdminPanelEnable" type="checkbox" onchange="document.TSFE_ADMIN_PANEL_FORM.submit();" name="TSFE_ADMIN_PANEL[display_top]" value="1"' .
+                    ($this->isAdminPanelActivated() ? ' checked="checked"' : '') .
+                    '/>';
         $output[] = '  <input id="typo3AdminPanelCollapse" type="checkbox" value="1" />';
-        $output[] = '  <div class="typo3-adminPanel typo3-adminPanel-state-' . ($this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] ? 'open' : 'closed') . '">';
+        $output[] = '  <div class="typo3-adminPanel typo3-adminPanel-state-' .
+                    ($this->isAdminPanelActivated() ? 'open' : 'closed') .
+                    '">';
         $output[] = '    <div class="typo3-adminPanel-header">';
         $output[] = '      <span class="typo3-adminPanel-header-title">' . $this->extGetLL('adminPanelTitle') . '</span>';
         $output[] = '      <span class="typo3-adminPanel-header-user">' . htmlspecialchars($this->getBackendUser()->user['username']) . '</span>';
@@ -408,7 +270,7 @@ class AdminPanelView
         }
         $cssFileLocation = GeneralUtility::getFileAbsFileName('EXT:frontend/Resources/Public/Css/adminpanel.css');
         $output[] = '<link type="text/css" rel="stylesheet" href="' . htmlspecialchars(PathUtility::getAbsoluteWebPath($cssFileLocation)) . '" media="all" />';
-        $output[] = $this->getAdminPanelHeaderData();
+        $output[] = $this->getAdminPanelStylesheet();
         $output[] = '<!-- TYPO3 admin panel end -->';
 
         return implode('', $output);
@@ -436,12 +298,52 @@ class AdminPanelView
         return $out;
     }
 
+    /**
+     * Returns true if admin panel was activated
+     * (switched "on" via GUI)
+     *
+     * @return bool
+     */
+    protected function isAdminPanelActivated(): bool
+    {
+        return $this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] ?? false;
+    }
+
+    /**
+     * Save admin panel configuration to backend user UC
+     */
+    protected function saveConfiguration()
+    {
+        $input = GeneralUtility::_GP('TSFE_ADMIN_PANEL');
+        $beUser = $this->getBackendUser();
+        if (is_array($input)) {
+            // Setting
+            $beUser->uc['TSFE_adminConfig'] = array_merge(
+                !is_array($beUser->uc['TSFE_adminConfig']) ? [] : $beUser->uc['TSFE_adminConfig'],
+                $input
+            );
+            unset($beUser->uc['TSFE_adminConfig']['action']);
+
+            foreach ($this->modules as $module) {
+                if ($module->isEnabled() && $module->isOpen()) {
+                    $module->onSubmit($input);
+                }
+            }
+            // Saving
+            $beUser->writeUC();
+            // Flush fluid template cache
+            $cacheManager = new CacheManager();
+            $cacheManager->setCacheConfigurations($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']);
+            $cacheManager->getCache('fluid_template')->flush();
+        }
+    }
+
     /**
      * Validates, sorts and initiates the registered modules
      *
      * @throws \RuntimeException
      */
-    protected function validateSortAndInitiateModules(): void
+    protected function validateSortAndInitializeModules(): void
     {
         $modules = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['frontend']['adminPanelModules'] ?? [];
         if (empty($modules)) {
@@ -484,63 +386,34 @@ class AdminPanelView
     /*****************************************************
      * Admin Panel Layout Helper functions
      ****************************************************/
-    /**
-     * Returns a row (with colspan=4) which is a header for a section in the Admin Panel.
-     * It will have a plus/minus icon and a label which is linked so that it submits the form which surrounds the whole Admin Panel when clicked, alterting the TSFE_ADMIN_PANEL[display_' . $pre . '] value
-     * See the functions get*Module
-     *
-     * @param string $sectionSuffix The suffix to the display_ label. Also selects the label from the LOCAL_LANG array.
-     * @return string HTML table row.
-     * @see extGetItem()
-     */
-    public function extGetHead($sectionSuffix)
-    {
-        return  $this->linkSectionHeader($sectionSuffix, $this->extGetLL($sectionSuffix));
-    }
 
     /**
      * Wraps a string in a link which will open/close a certain part of the Admin Panel
      *
-     * @param string $sectionSuffix The code for the display_ label/key
-     * @param string $sectionTitle Title (HTML-escaped)
-     * @param string $className The classname for the <a> tag
-     * @return string $className Linked input string
-     * @see extGetHead()
+     * @param \TYPO3\CMS\Frontend\AdminPanel\AdminPanelModuleInterface $module
+     * @return string
      */
-    public function linkSectionHeader($sectionSuffix, $sectionTitle, $className = '')
+    protected function getSectionOpenerLink(AdminPanelModuleInterface $module): string
     {
-        $onclick = 'document.TSFE_ADMIN_PANEL_FORM[' . GeneralUtility::quoteJSvalue('TSFE_ADMIN_PANEL[display_' . $sectionSuffix . ']') . '].value=' . ($this->getBackendUser()->uc['TSFE_adminConfig']['display_' . $sectionSuffix] ? '0' : '1') . ';document.TSFE_ADMIN_PANEL_FORM.submit();return false;';
+        $identifier = $module->getIdentifier();
+        $onclick = 'document.TSFE_ADMIN_PANEL_FORM[' .
+                   GeneralUtility::quoteJSvalue('TSFE_ADMIN_PANEL[display_' . $identifier . ']') .
+                   '].value=' .
+                   ($this->getBackendUser()->uc['TSFE_adminConfig']['display_' . $identifier] ? '0' : '1') .
+                   ';document.TSFE_ADMIN_PANEL_FORM.submit();return false;';
 
         $output = [];
         $output[] = '<span class="typo3-adminPanel-section-title-identifier"></span>';
         $output[] = '<a href="javascript:void(0)" onclick="' . htmlspecialchars($onclick) . '">';
-        $output[] = '  ' . $sectionTitle;
+        $output[] = '  ' . htmlspecialchars($module->getLabel());
         $output[] = '</a>';
-        $output[] = '<input type="hidden" name="TSFE_ADMIN_PANEL[display_' . $sectionSuffix . ']" value="' . (int)$this->isAdminModuleOpen($sectionSuffix) . '" />';
-
-        return  implode('', $output);
-    }
+        $output[] = '<input type="hidden" name="TSFE_ADMIN_PANEL[display_' .
+                    $identifier .
+                    ']" value="' .
+                    (int)$module->isOpen() .
+                    '" />';
 
-    /**
-     * Returns a row (with 4 columns) for content in a section of the Admin Panel.
-     * It will take $pre as a key to a label to display and $element as the content to put into the forth cell.
-     *
-     * @param string $title Key to label
-     * @param string $content The HTML content for the forth table cell.
-     * @param string $checkbox The HTML for a checkbox or hidden fields.
-     * @param string $innerDivClass The Class attribute for the td element.
-     * @param string $outerDivClass The Class attribute for the tr element.
-     * @return string HTML table row.
-     * @see extGetHead()
-     */
-    public function extGetItem($title, $content = '', $checkbox = '', $outerDivClass = null, $innerDivClass = null)
-    {
-        $title = $title ? '<label for="' . htmlspecialchars($title) . '">' . $this->extGetLL($title) . '</label>' : '';
-        $out = '';
-        $out .= (string)$outerDivClass ? '<div class="' . htmlspecialchars($outerDivClass) . '">' : '<div>';
-        $out .= (string)$innerDivClass ? '<div class="' . htmlspecialchars($innerDivClass) . '">' : '<div>';
-        $out .= $checkbox . $title . $content . '</div></div>';
-        return $out;
+        return implode('', $output);
     }
 
     /**
@@ -750,11 +623,205 @@ class AdminPanelView
         return $GLOBALS['TSFE'];
     }
 
+    /*****************************************************
+     * Admin Panel: Deprecated API
+     ****************************************************/
+
+    /**
+     * Add an additional stylesheet
+     *
+     * @return string
+     * @deprecated since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
+     */
+    public function getAdminPanelHeaderData()
+    {
+        trigger_error(
+            'Deprecated since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)',
+            E_USER_DEPRECATED
+        );
+        return $this->getAdminPanelStylesheet();
+    }
+
+    /**
+     * Checks if an Admin Panel section ("module") is available for the user. If so, TRUE is returned.
+     *
+     * @param string $key The module key, eg. "edit", "preview", "info" etc.
+     * @deprecated but still called in FrontendBackendUserAuthentication (will be refactored in a separate step)
+     * @return bool
+     */
+    public function isAdminModuleEnabled($key)
+    {
+        $result = false;
+        // Returns TRUE if the module checked is "preview" and the forcePreview flag is set.
+        if ($key === 'preview' && $this->ext_forcePreview) {
+            $result = true;
+        } elseif (!empty($this->getBackendUser()->extAdminConfig['enable.']['all'])) {
+            $result = true;
+        } elseif (!empty($this->getBackendUser()->extAdminConfig['enable.'][$key])) {
+            $result = true;
+        }
+        return $result;
+    }
+
     /**
-     * @return TimeTracker
+     * Saves any change in settings made in the Admin Panel.
+     *
+     * @deprecated since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
      */
-    protected function getTimeTracker()
+    public function saveConfigOptions()
     {
-        return GeneralUtility::makeInstance(TimeTracker::class);
+        trigger_error(
+            'Deprecated since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)',
+            E_USER_DEPRECATED
+        );
+        $this->saveConfiguration();
+    }
+
+    /**
+     * Returns the value for an Admin Panel setting.
+     *
+     * @param string $sectionName Module key
+     * @param string $val Setting key
+     * @return mixed The setting value
+     * @deprecated Since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)
+     */
+    public function extGetFeAdminValue($sectionName, $val = '')
+    {
+        trigger_error(
+            'Deprecated since TYPO3 v9 - implement AdminPanelModules via the new API (see AdminPanelModuleInterface)',
+            E_USER_DEPRECATED
+        );
+        if (!$this->isAdminModuleEnabled($sectionName)) {
+            return null;
+        }
+
+        $beUser = $this->getBackendUser();
+        // Exceptions where the values can be overridden (forced) from backend:
+        // deprecated
+        if (
+            $sectionName === 'edit' && (
+                $val === 'displayIcons' && $beUser->extAdminConfig['module.']['edit.']['forceDisplayIcons'] ||
+                $val === 'displayFieldIcons' && $beUser->extAdminConfig['module.']['edit.']['forceDisplayFieldIcons'] ||
+                $val === 'editNoPopup' && $beUser->extAdminConfig['module.']['edit.']['forceNoPopup']
+            )
+        ) {
+            return true;
+        }
+
+        // Override all settings with user TSconfig
+        if ($val && isset($beUser->extAdminConfig['override.'][$sectionName . '.'][$val])) {
+            return $beUser->extAdminConfig['override.'][$sectionName . '.'][$val];
+        }
+        if (!$val && isset($beUser->extAdminConfig['override.'][$sectionName])) {
+            return $beUser->extAdminConfig['override.'][$sectionName];
+        }
+
+        $returnValue = $val ? $beUser->uc['TSFE_adminConfig'][$sectionName . '_' . $val] : 1;
+
+        // Exception for preview
+        if ($sectionName === 'preview' && $this->ext_forcePreview) {
+            return !$val ? true : $returnValue;
+        }
+
+        // See if the menu is expanded!
+        return $this->isAdminModuleOpen($sectionName) ? $returnValue : null;
+    }
+
+    /**
+     * Enables the force preview option.
+     *
+     * @deprecated since TYPO3 v9 - see AdminPanelModule: Preview
+     */
+    public function forcePreview()
+    {
+        trigger_error('Deprecated since TYPO3 v9, see AdminPanelModule: Preview', E_USER_DEPRECATED);
+        $this->ext_forcePreview = true;
+    }
+
+    /**
+     * Returns TRUE if admin panel module is open
+     *
+     * @param string $key Module key
+     * @return bool TRUE, if the admin panel is open for the specified admin panel module key.
+     * @deprecated Since TYPO3 v9 - implement AdminPanelModules via the new API
+     */
+    public function isAdminModuleOpen($key)
+    {
+        trigger_error('since TYPO3 v9 - use new AdminPanel API instead', E_USER_DEPRECATED);
+        return $this->getBackendUser()->uc['TSFE_adminConfig']['display_top'] &&
+               $this->getBackendUser()->uc['TSFE_adminConfig']['display_' . $key];
+    }
+
+    /**
+     * Returns a row (with 4 columns) for content in a section of the Admin Panel.
+     * It will take $pre as a key to a label to display and $element as the content to put into the forth cell.
+     *
+     * @param string $title Key to label
+     * @param string $content The HTML content for the forth table cell.
+     * @param string $checkbox The HTML for a checkbox or hidden fields.
+     * @param string $innerDivClass The Class attribute for the td element.
+     * @param string $outerDivClass The Class attribute for the tr element.
+     * @return string HTML table row.
+     * @see extGetHead()
+     * @deprecated since TYPO3 v9 - use new AdminPanel API instead
+     */
+    public function extGetItem($title, $content = '', $checkbox = '', $outerDivClass = null, $innerDivClass = null)
+    {
+        trigger_error('since TYPO3 v9 - use new AdminPanel API instead', E_USER_DEPRECATED);
+        $title = $title ? '<label for="' . htmlspecialchars($title) . '">' . $this->extGetLL($title) . '</label>' : '';
+        $out = '';
+        $out .= (string)$outerDivClass ? '<div class="' . htmlspecialchars($outerDivClass) . '">' : '<div>';
+        $out .= (string)$innerDivClass ? '<div class="' . htmlspecialchars($innerDivClass) . '">' : '<div>';
+        $out .= $checkbox . $title . $content . '</div></div>';
+        return $out;
+    }
+
+    /**
+     * Returns a row (with colspan=4) which is a header for a section in the Admin Panel.
+     * It will have a plus/minus icon and a label which is linked so that it submits the form which surrounds the whole Admin Panel when clicked, alterting the TSFE_ADMIN_PANEL[display_' . $pre . '] value
+     * See the functions get*Module
+     *
+     * @param string $sectionSuffix The suffix to the display_ label. Also selects the label from the LOCAL_LANG array.
+     * @return string HTML table row.
+     * @see extGetItem()
+     * @deprecated since TYPO3 v9 - use new AdminPanel API instead
+     */
+    public function extGetHead($sectionSuffix)
+    {
+        trigger_error('since TYPO3 v9 - use new AdminPanel API instead', E_USER_DEPRECATED);
+        return $this->linkSectionHeader($sectionSuffix, $this->extGetLL($sectionSuffix));
+    }
+
+    /**
+     * Wraps a string in a link which will open/close a certain part of the Admin Panel
+     *
+     * @param string $sectionSuffix The code for the display_ label/key
+     * @param string $sectionTitle Title (HTML-escaped)
+     * @param string $className The classname for the <a> tag
+     * @return string $className Linked input string
+     * @see extGetHead()
+     * @deprecated  since TYPO3 v9 - use new AdminPanel API instead
+     */
+    public function linkSectionHeader($sectionSuffix, $sectionTitle, $className = '')
+    {
+        trigger_error('since TYPO3 v9 - use new AdminPanel API instead', E_USER_DEPRECATED);
+        $onclick = 'document.TSFE_ADMIN_PANEL_FORM[' .
+                   GeneralUtility::quoteJSvalue('TSFE_ADMIN_PANEL[display_' . $sectionSuffix . ']') .
+                   '].value=' .
+                   ($this->getBackendUser()->uc['TSFE_adminConfig']['display_' . $sectionSuffix] ? '0' : '1') .
+                   ';document.TSFE_ADMIN_PANEL_FORM.submit();return false;';
+
+        $output = [];
+        $output[] = '<span class="typo3-adminPanel-section-title-identifier"></span>';
+        $output[] = '<a href="javascript:void(0)" onclick="' . htmlspecialchars($onclick) . '">';
+        $output[] = '  ' . $sectionTitle;
+        $output[] = '</a>';
+        $output[] = '<input type="hidden" name="TSFE_ADMIN_PANEL[display_' .
+                    $sectionSuffix .
+                    ']" value="' .
+                    (int)$this->isAdminModuleOpen($sectionSuffix) .
+                    '" />';
+
+        return implode('', $output);
     }
 }
diff --git a/typo3/sysext/frontend/Tests/Unit/View/AdminPanelViewTest.php b/typo3/sysext/frontend/Tests/Unit/View/AdminPanelViewTest.php
index 6daf31e3b0fb..0561280d48c1 100644
--- a/typo3/sysext/frontend/Tests/Unit/View/AdminPanelViewTest.php
+++ b/typo3/sysext/frontend/Tests/Unit/View/AdminPanelViewTest.php
@@ -1,4 +1,5 @@
 <?php
+declare(strict_types=1);
 namespace TYPO3\CMS\Frontend\Tests\Unit\View;
 
 /*
@@ -13,63 +14,63 @@ namespace TYPO3\CMS\Frontend\Tests\Unit\View;
  *
  * The TYPO3 project - inspiring people to share!
  */
-use TYPO3\CMS\Core\Cache\CacheManager;
-use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
-use TYPO3\CMS\Core\Localization\LanguageService;
+
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Tests\Unit\View\Fixtures\AdminPanelDisabledModuleFixture;
+use TYPO3\CMS\Frontend\Tests\Unit\View\Fixtures\AdminPanelEnabledShownOnSubmitInitializeModuleFixture;
+use TYPO3\CMS\Frontend\View\AdminPanelView;
 
 /**
  * Test case
  */
 class AdminPanelViewTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
 {
-    /**
-     * Subject is not notice free, disable E_NOTICES
-     */
-    protected static $suppressNotices = true;
+    public function setUp()
+    {
+        parent::setUp();
+        $iconFactoryProphecy = $this->prophesize(IconFactory::class);
+        GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
+        $beUserProphecy = $this->prophesize(BackendUserAuthentication::class);
+        $GLOBALS['BE_USER'] = $beUserProphecy->reveal();
+    }
 
     /**
-     * Set up
+     * @test
      */
-    protected function setUp()
+    public function initializeCallsOnSubmitIfInputVarsAreSet()
     {
-        $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
-        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
-        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
-        $cacheManagerProphecy->getCache('cache_pages')->willReturn($cacheFrontendProphecy->reveal());
-        $GLOBALS['TSFE'] = new TypoScriptFrontendController([], 1, 1);
-    }
+        $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['frontend']['adminPanelModules'] = [
+            'fixtureOnSubmit' => [
+                'module' => AdminPanelEnabledShownOnSubmitInitializeModuleFixture::class
+            ]
+        ];
 
-    protected function tearDown()
-    {
-        GeneralUtility::purgeInstances();
-        parent::tearDown();
+        $postVars = ['preview_showFluidDebug' => '1'];
+        $_GET['TSFE_ADMIN_PANEL'] = $postVars;
+
+        $this->expectExceptionCode('1519997815');
+
+        new AdminPanelView();
     }
 
     /**
      * @test
      */
-    public function extGetFeAdminValueReturnsTimestamp()
+    public function initializeCallsInitializeModulesForEnabledModules()
     {
-        $strTime = '2013-01-01 01:00:00';
-        $timestamp = strtotime($strTime);
-
-        $backendUser = $this->getMockBuilder(\TYPO3\CMS\Core\Authentication\BackendUserAuthentication::class)->getMock();
-        $backendUser->uc['TSFE_adminConfig']['preview_simulateDate'] = $timestamp;
-        unset($backendUser->extAdminConfig['override.']['preview.']);
-        unset($backendUser->extAdminConfig['override.']['preview']);
-        $GLOBALS['BE_USER'] = $backendUser;
-
-        $adminPanelMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\View\AdminPanelView::class)
-            ->setMethods(['isAdminModuleEnabled', 'isAdminModuleOpen'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $adminPanelMock->expects($this->any())->method('isAdminModuleEnabled')->will($this->returnValue(true));
-        $adminPanelMock->expects($this->any())->method('isAdminModuleOpen')->will($this->returnValue(true));
+        $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['frontend']['adminPanelModules'] = [
+            'enabledModule' => [
+                'module' => AdminPanelEnabledShownOnSubmitInitializeModuleFixture::class
+            ],
+            'disabledModule' => [
+                'module' => AdminPanelDisabledModuleFixture::class,
+                'before' => ['enabledModule']
+            ]
+        ];
 
-        $timestampReturned = $adminPanelMock->extGetFeAdminValue('preview', 'simulateDate');
-        $this->assertEquals($timestamp, $timestampReturned);
+        $this->expectExceptionCode(1519999273);
+        new AdminPanelView();
     }
 }
diff --git a/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelDisabledModuleFixture.php b/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelDisabledModuleFixture.php
new file mode 100644
index 000000000000..e93556c006fa
--- /dev/null
+++ b/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelDisabledModuleFixture.php
@@ -0,0 +1,131 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Frontend\Tests\Unit\View\Fixtures;
+
+/*
+ * 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!
+ */
+use TYPO3\CMS\Frontend\AdminPanel\AdminPanelModuleInterface;
+
+class AdminPanelDisabledModuleFixture implements AdminPanelModuleInterface
+{
+
+    /**
+     * Additional JavaScript code for this module
+     * (you should only use vanilla JS here, as you cannot
+     * rely on the web site providing a specific framework)
+     *
+     * @return string
+     */
+    public function getAdditionalJavaScriptCode(): string
+    {
+        return '';
+    }
+
+    /**
+     * Module content as rendered HTML
+     *
+     * @return string
+     */
+    public function getContent(): string
+    {
+        return '';
+    }
+
+    /**
+     * Identifier for this module,
+     * for example "preview" or "cache"
+     *
+     * @return string
+     */
+    public function getIdentifier(): string
+    {
+        return '';
+    }
+
+    /**
+     * Module label
+     *
+     * @return string
+     */
+    public function getLabel(): string
+    {
+        return '';
+    }
+
+    /**
+     * Initialize the module - runs early in a TYPO3 request
+     */
+    public function initializeModule(): void
+    {
+        throw new \RuntimeException('I should not be initialized.', 1519999375);
+    }
+
+    /**
+     * Module is enabled
+     * -> should be initialized
+     * A module may be enabled but not shown
+     * -> only the initializeModule() method
+     * will be called
+     *
+     * @return bool
+     */
+    public function isEnabled(): bool
+    {
+        return false;
+    }
+
+    /**
+     * Module is open
+     * -> module is enabled
+     * -> module panel is shown and open
+     *
+     * @return bool
+     */
+    public function isOpen(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Module is shown
+     * -> module is enabled
+     * -> module panel should be displayed
+     *
+     * @return bool
+     */
+    public function isShown(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Executed on saving / submit of the configuration form
+     * Can be used to react to changed settings
+     * (for example: clearing a specific cache)
+     *
+     * @param array $input
+     */
+    public function onSubmit(array $input): void
+    {
+    }
+
+    /**
+     * Does this module need a form submit?
+     *
+     * @return bool
+     */
+    public function showFormSubmitButton(): bool
+    {
+        return true;
+    }
+}
diff --git a/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelEnabledShownOnSubmitInitializeModuleFixture.php b/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelEnabledShownOnSubmitInitializeModuleFixture.php
new file mode 100644
index 000000000000..1572fdf7dd48
--- /dev/null
+++ b/typo3/sysext/frontend/Tests/Unit/View/Fixtures/AdminPanelEnabledShownOnSubmitInitializeModuleFixture.php
@@ -0,0 +1,132 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Frontend\Tests\Unit\View\Fixtures;
+
+/*
+ * 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!
+ */
+use TYPO3\CMS\Frontend\AdminPanel\AdminPanelModuleInterface;
+
+class AdminPanelEnabledShownOnSubmitInitializeModuleFixture implements AdminPanelModuleInterface
+{
+
+    /**
+     * Additional JavaScript code for this module
+     * (you should only use vanilla JS here, as you cannot
+     * rely on the web site providing a specific framework)
+     *
+     * @return string
+     */
+    public function getAdditionalJavaScriptCode(): string
+    {
+        return '';
+    }
+
+    /**
+     * Module content as rendered HTML
+     *
+     * @return string
+     */
+    public function getContent(): string
+    {
+        return '';
+    }
+
+    /**
+     * Identifier for this module,
+     * for example "preview" or "cache"
+     *
+     * @return string
+     */
+    public function getIdentifier(): string
+    {
+        return '';
+    }
+
+    /**
+     * Module label
+     *
+     * @return string
+     */
+    public function getLabel(): string
+    {
+        return '';
+    }
+
+    /**
+     * Initialize the module - runs early in a TYPO3 request
+     */
+    public function initializeModule(): void
+    {
+        throw new \RuntimeException('initialized.', 1519999273);
+    }
+
+    /**
+     * Module is enabled
+     * -> should be initialized
+     * A module may be enabled but not shown
+     * -> only the initializeModule() method
+     * will be called
+     *
+     * @return bool
+     */
+    public function isEnabled(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Module is open
+     * -> module is enabled
+     * -> module panel is shown and open
+     *
+     * @return bool
+     */
+    public function isOpen(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Module is shown
+     * -> module is enabled
+     * -> module panel should be displayed
+     *
+     * @return bool
+     */
+    public function isShown(): bool
+    {
+        return true;
+    }
+
+    /**
+     * Executed on saving / submit of the configuration form
+     * Can be used to react to changed settings
+     * (for example: clearing a specific cache)
+     *
+     * @param array $input
+     */
+    public function onSubmit(array $input): void
+    {
+        throw new \RuntimeException('Catch me if you can!', 1519997815);
+    }
+
+    /**
+     * Does this module need a form submit?
+     *
+     * @return bool
+     */
+    public function showFormSubmitButton(): bool
+    {
+        return true;
+    }
+}
diff --git a/typo3/sysext/frontend/Tests/UnitDeprecated/View/AdminPanelViewTest.php b/typo3/sysext/frontend/Tests/UnitDeprecated/View/AdminPanelViewTest.php
deleted file mode 100644
index 0f548c932915..000000000000
--- a/typo3/sysext/frontend/Tests/UnitDeprecated/View/AdminPanelViewTest.php
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-namespace TYPO3\CMS\Frontend\Tests\UnitDeprecated\View;
-
-/*
- * 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!
- */
-use Prophecy\Argument;
-use TYPO3\CMS\Core\Cache\CacheManager;
-use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
-use TYPO3\CMS\Core\Imaging\Icon;
-use TYPO3\CMS\Core\Imaging\IconFactory;
-use TYPO3\CMS\Core\Localization\LanguageService;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
-
-/**
- * Test case
- */
-class AdminPanelViewTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
-{
-    /**
-     * Subject is not notice free, disable E_NOTICES
-     */
-    protected static $suppressNotices = true;
-
-    /**
-     * Set up
-     */
-    protected function setUp()
-    {
-        $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
-        $cacheManagerProphecy = $this->prophesize(CacheManager::class);
-        GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
-        $cacheFrontendProphecy = $this->prophesize(FrontendInterface::class);
-        $cacheManagerProphecy->getCache('cache_pages')->willReturn($cacheFrontendProphecy->reveal());
-        $GLOBALS['TSFE'] = new TypoScriptFrontendController([], 1, 1);
-    }
-
-    protected function tearDown()
-    {
-        GeneralUtility::purgeInstances();
-        parent::tearDown();
-    }
-
-    /////////////////////////////////////////////
-    // Test concerning extendAdminPanel hook
-    /////////////////////////////////////////////
-
-    /**
-     * @test
-     */
-    public function extendAdminPanelHookThrowsExceptionIfHookClassDoesNotImplementInterface()
-    {
-        $this->expectException(\UnexpectedValueException::class);
-        $this->expectExceptionCode(1311942539);
-        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_adminpanel.php']['extendAdminPanel'][] = \TYPO3\CMS\Frontend\Tests\Unit\Fixtures\AdminPanelHookWithoutInterfaceFixture::class;
-        /** @var $adminPanelMock \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\View\AdminPanelView */
-        $adminPanelMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\View\AdminPanelView::class)
-            ->setMethods(['dummy'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $adminPanelMock->display();
-    }
-
-    /**
-     * @test
-     */
-    public function extendAdminPanelHookCallsExtendAdminPanelMethodOfHook()
-    {
-        $hookClass = $this->getUniqueId('tx_coretest');
-        $hookMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\View\AdminPanelViewHookInterface::class)
-            ->setMockClassName($hookClass)
-            ->getMock();
-        GeneralUtility::addInstance($hookClass, $hookMock);
-        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_adminpanel.php']['extendAdminPanel'][] = $hookClass;
-        /** @var $adminPanelMock \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Frontend\View\AdminPanelView */
-        $adminPanelMock = $this->getMockBuilder(\TYPO3\CMS\Frontend\View\AdminPanelView::class)
-            ->setMethods(['extGetLL'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $iconFactoryProphecy = $this->prophesize(IconFactory::class);
-        GeneralUtility::addInstance(IconFactory::class, $iconFactoryProphecy->reveal());
-        $iconProphecy = $this->prophesize(Icon::class);
-        $iconFactoryProphecy->getIcon(Argument::cetera())->willReturn($iconProphecy->reveal());
-        $iconProphecy->render(Argument::cetera())->willReturn('');
-        $adminPanelMock->initialize();
-        $hookMock->expects($this->once())->method('extendAdminPanel')->with($this->isType('string'), $this->isInstanceOf(\TYPO3\CMS\Frontend\View\AdminPanelView::class));
-        $adminPanelMock->display();
-    }
-}
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
index 08427a5afa8c..fdd91a7cb625 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
@@ -1717,4 +1717,67 @@ return [
             'Important-83869-RemovedRequestTypeSpecificCodeInBootstrap.rst',
         ],
     ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->getAdminPanelHeaderData' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->isAdminModuleEnabled' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->saveConfigOptions' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->extGetFeAdminValue' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 2,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->forcePreview' => [
+        'numberOfMandatoryArguments' => 0,
+        'maximumNumberOfArguments' => 0,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->isAdminModuleOpen' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->extGetHead' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->linkSectionHeader' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
+    'TYPO3\CMS\Frontend\View\AdminPanelView->extGetItem' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 5,
+        'restFiles' => [
+            'Deprecation-84118-VariousPublicMethodsOfAdminPanelViewDeprecated.rst',
+        ],
+    ],
 ];
-- 
GitLab