From f279ceb4d067cf49250598fa75a5d4f75d5268c2 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Wed, 9 Feb 2022 16:47:04 +0100
Subject: [PATCH] [!!!][FEATURE] Add PSR-14 ModifyButtonBarEvent

The new PSR-14 event ModifyButtonBarEvent is added
as a direct replacement for the now removed hook
$TYPO3_CONF_VARS[SC_OPTIONS][Backend\Template\Components\ButtonBar][getButtonsHook]

Resolves: #96806
Releases: main
Change-Id: Ib743cc532a78a3e38c96e7a89ba2e3856d8cbd0d
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/73380
Tested-by: core-ci <typo3@b13.com>
Tested-by: Jochen <rothjochen@gmail.com>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Jochen <rothjochen@gmail.com>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
---
 .../Classes/Template/Components/ButtonBar.php | 16 +++---
 .../Components/ModifyButtonBarEvent.php       | 54 +++++++++++++++++++
 ...06-RemovedHookForForModifyingButtonBar.rst | 38 +++++++++++++
 ...96806-PSR-14EventForModifyingButtonBar.rst | 50 +++++++++++++++++
 .../Php/ArrayDimensionMatcher.php             |  5 ++
 .../ButtonBarProvider.php}                    | 23 ++++----
 .../sys_note/Configuration/Services.yaml      |  4 ++
 typo3/sysext/sys_note/ext_localconf.php       |  3 --
 8 files changed, 169 insertions(+), 24 deletions(-)
 create mode 100644 typo3/sysext/backend/Classes/Template/Components/ModifyButtonBarEvent.php
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.0/Breaking-96806-RemovedHookForForModifyingButtonBar.rst
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.0/Feature-96806-PSR-14EventForModifyingButtonBar.rst
 rename typo3/sysext/sys_note/Classes/{Hook/ButtonBarHook.php => Provider/ButtonBarProvider.php} (89%)

diff --git a/typo3/sysext/backend/Classes/Template/Components/ButtonBar.php b/typo3/sysext/backend/Classes/Template/Components/ButtonBar.php
index 02852a8d7283..783682d788a4 100644
--- a/typo3/sysext/backend/Classes/Template/Components/ButtonBar.php
+++ b/typo3/sysext/backend/Classes/Template/Components/ButtonBar.php
@@ -15,6 +15,7 @@
 
 namespace TYPO3\CMS\Backend\Template\Components;
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use TYPO3\CMS\Backend\Template\Components\Buttons\Action\HelpButton;
 use TYPO3\CMS\Backend\Template\Components\Buttons\Action\ShortcutButton;
 use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface;
@@ -160,19 +161,18 @@ class ButtonBar
      *
      * @return array
      */
-    public function getButtons()
+    public function getButtons(): array
     {
         // here we need to call the sorting methods and stuff.
         foreach ($this->buttons as $position => $_) {
             ksort($this->buttons[$position]);
         }
-        // Hook for manipulating the docHeaderButtons
-        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend\Template\Components\ButtonBar']['getButtonsHook'] ?? [] as $funcRef) {
-            $params = [
-                'buttons' => $this->buttons,
-            ];
-            $this->buttons = GeneralUtility::callUserFunction($funcRef, $params, $this);
-        }
+
+        // Dispatch event for manipulating the docHeaderButtons
+        $this->buttons = GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
+            new ModifyButtonBarEvent($this->buttons, $this)
+        )->getButtons();
+
         return $this->buttons;
     }
 }
diff --git a/typo3/sysext/backend/Classes/Template/Components/ModifyButtonBarEvent.php b/typo3/sysext/backend/Classes/Template/Components/ModifyButtonBarEvent.php
new file mode 100644
index 000000000000..5c198ae69d48
--- /dev/null
+++ b/typo3/sysext/backend/Classes/Template/Components/ModifyButtonBarEvent.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Backend\Template\Components;
+
+use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface;
+
+/**
+ * Listeners can modify the buttons of the button bar in the backend module docheader
+ */
+final class ModifyButtonBarEvent
+{
+    /**
+     * @var ButtonInterface[]
+     */
+    private array $buttons;
+
+    private ButtonBar $buttonBar;
+
+    public function __construct(array $buttons, ButtonBar $buttonBar)
+    {
+        $this->buttons = $buttons;
+        $this->buttonBar = $buttonBar;
+    }
+
+    public function getButtons(): array
+    {
+        return $this->buttons;
+    }
+
+    public function setButtons(array $buttons): void
+    {
+        $this->buttons = $buttons;
+    }
+
+    public function getButtonBar(): ButtonBar
+    {
+        return $this->buttonBar;
+    }
+}
diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-96806-RemovedHookForForModifyingButtonBar.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-96806-RemovedHookForForModifyingButtonBar.rst
new file mode 100644
index 000000000000..7f8bdd739724
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-96806-RemovedHookForForModifyingButtonBar.rst
@@ -0,0 +1,38 @@
+.. include:: ../../Includes.txt
+
+============================================================
+Breaking: #96806 - Removed hook for for modifying button bar
+============================================================
+
+See :issue:`96806`
+
+Description
+===========
+
+The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend\Template\Components\ButtonBar']['getButtonsHook']`
+has been removed in favor of a new PSR-14 Event :php:`\TYPO3\CMS\Backend\Template\Components\ModifyButtonBarEvent`.
+
+Impact
+======
+
+Any hook implementation registered is not executed anymore in
+TYPO3 v12.0+. The extension scanner will report possible usages.
+
+
+Affected Installations
+======================
+
+All TYPO3 installations using this hook in custom extension code.
+
+
+Migration
+=========
+
+The hook is removed without deprecation in order to allow extensions
+to work with TYPO3 v11 (using the hook) and v12+ (using the new Event)
+when implementing the Event as well without any further deprecations.
+
+Use the :doc:`PSR-14 Event <../12.0/Feature-96806-PSR-14EventForModifyingButtonBar>`
+as a direct replacement.
+
+.. index:: Backend, PHP-API, FullyScanned, ext:backend
diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Feature-96806-PSR-14EventForModifyingButtonBar.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-96806-PSR-14EventForModifyingButtonBar.rst
new file mode 100644
index 000000000000..8a58e64a1bdb
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.0/Feature-96806-PSR-14EventForModifyingButtonBar.rst
@@ -0,0 +1,50 @@
+.. include:: ../../Includes.txt
+
+=======================================================
+Feature: #96806 - PSR-14 Event for modifying button bar
+=======================================================
+
+See :issue:`96806`
+
+Description
+===========
+
+A new PSR-14 Event :php:`\TYPO3\CMS\Backend\Template\Components\ModifyButtonBarEvent`
+has been introduced. It serves as direct replacement for the now removed hook
+:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend\Template\Components\ButtonBar']['getButtonsHook']`.
+
+It can be used to modify the button bar in the TYPO3 backend module docheader.
+
+Example
+=======
+
+Registration of the Event in your extensions' :file:`Services.yaml`:
+
+.. code-block:: yaml
+
+  MyVendor\MyPackage\Frontend\MyEventListener:
+    tags:
+      - name: event.listener
+        identifier: 'my-package/frontend/modify-button-bar'
+
+The corresponding event listener class:
+
+.. code-block:: php
+
+    use TYPO3\CMS\Backend\Template\Components\ModifyButtonBarEvent;
+
+    class MyEventListener {
+
+        public function __invoke(ModifyButtonBarEvent $event): void
+        {
+            // Do your magic here
+        }
+    }
+
+Impact
+======
+
+It's now possible to modify the TYPO3 backend button bar, using the
+new PSR-14 event.
+
+.. index:: Backend, PHP-API, ext:backend
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
index 4ea68235f1dd..31481a041260 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
@@ -599,4 +599,9 @@ return [
             'Breaking-96659-RegistrationOfCObjectsViaTYPO3_CONF_VARS.rst',
         ],
     ],
+    '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'Backend\Template\Components\ButtonBar\'][\'getButtonsHook\']' => [
+        'restFiles' => [
+            'Breaking-96806-RemovedHookForForModifyingButtonBar.rst',
+        ],
+    ],
 ];
diff --git a/typo3/sysext/sys_note/Classes/Hook/ButtonBarHook.php b/typo3/sysext/sys_note/Classes/Provider/ButtonBarProvider.php
similarity index 89%
rename from typo3/sysext/sys_note/Classes/Hook/ButtonBarHook.php
rename to typo3/sysext/sys_note/Classes/Provider/ButtonBarProvider.php
index db2bbd62f82d..e2195501929d 100644
--- a/typo3/sysext/sys_note/Classes/Hook/ButtonBarHook.php
+++ b/typo3/sysext/sys_note/Classes/Provider/ButtonBarProvider.php
@@ -15,12 +15,13 @@ declare(strict_types=1);
  * The TYPO3 project - inspiring people to share!
  */
 
-namespace TYPO3\CMS\SysNote\Hook;
+namespace TYPO3\CMS\SysNote\Provider;
 
 use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
 use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
+use TYPO3\CMS\Backend\Template\Components\ModifyButtonBarEvent;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Imaging\Icon;
@@ -30,11 +31,11 @@ use TYPO3\CMS\Core\Type\Bitmask\Permission;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
- * Hook for the button bar
+ * Event listener to add the sys_note button to the button bar
  *
- * @internal This is a specific hook implementation and is not considered part of the Public TYPO3 API.
+ * @internal This is a specific listener implementation and is not considered part of the Public TYPO3 API.
  */
-class ButtonBarHook
+final class ButtonBarProvider
 {
     private const TABLE_NAME = 'sys_note';
     private const ALLOWED_MODULES = ['web_layout', 'web_list', 'web_info'];
@@ -42,15 +43,11 @@ class ButtonBarHook
     /**
      * Add a sys_note creation button to the button bar of defined modules
      *
-     * @param array $params
-     * @param ButtonBar $buttonBar
-     *
-     * @return array
      * @throws RouteNotFoundException
      */
-    public function getButtons(array $params, ButtonBar $buttonBar): array
+    public function __invoke(ModifyButtonBarEvent $event): void
     {
-        $buttons = $params['buttons'];
+        $buttons = $event->getButtons();
         $request = $this->getRequest();
 
         $id = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
@@ -66,7 +63,7 @@ class ButtonBarHook
             || !in_array($module->getIdentifier(), self::ALLOWED_MODULES, true)
             || ($module->getIdentifier() === 'web_list' && !$this->isCreationAllowed($pageTSconfig['mod.']['web_list.'] ?? []))
         ) {
-            return $buttons;
+            return;
         }
 
         $uri = (string)GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute(
@@ -81,7 +78,7 @@ class ButtonBarHook
             ]
         );
 
-        $buttons[ButtonBar::BUTTON_POSITION_RIGHT][2][] = $buttonBar
+        $buttons[ButtonBar::BUTTON_POSITION_RIGHT][2][] = $event->getButtonBar()
             ->makeLinkButton()
             ->setTitle(htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:sys_note/Resources/Private/Language/locallang.xlf:new_internal_note')))
             ->setIcon(GeneralUtility::makeInstance(IconFactory::class)->getIcon('sysnote-type-0', Icon::SIZE_SMALL))
@@ -89,7 +86,7 @@ class ButtonBarHook
 
         ksort($buttons[ButtonBar::BUTTON_POSITION_RIGHT]);
 
-        return $buttons;
+        $event->setButtons($buttons);
     }
 
     /**
diff --git a/typo3/sysext/sys_note/Configuration/Services.yaml b/typo3/sysext/sys_note/Configuration/Services.yaml
index 5de200e5e6ee..1859b6ef463e 100644
--- a/typo3/sysext/sys_note/Configuration/Services.yaml
+++ b/typo3/sysext/sys_note/Configuration/Services.yaml
@@ -18,3 +18,7 @@ services:
     tags:
       - name: event.listener
         identifier: 'note-to-page-module'
+  TYPO3\CMS\SysNote\Provider\ButtonBarProvider:
+    tags:
+      - name: event.listener
+        identifier: 'note-to-button-bar'
diff --git a/typo3/sysext/sys_note/ext_localconf.php b/typo3/sysext/sys_note/ext_localconf.php
index 8282c212f060..d074f5e1f200 100644
--- a/typo3/sysext/sys_note/ext_localconf.php
+++ b/typo3/sysext/sys_note/ext_localconf.php
@@ -2,12 +2,9 @@
 
 declare(strict_types=1);
 
-use TYPO3\CMS\SysNote\Hook\ButtonBarHook;
 use TYPO3\CMS\SysNote\Hook\InfoModuleHook;
 
 defined('TYPO3') or die();
 
 // Hook into the info module
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/web_info/class.tx_cms_webinfo.php']['drawFooterHook']['sys_note'] = InfoModuleHook::class . '->render';
-// Hook into the button bar
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend\Template\Components\ButtonBar']['getButtonsHook']['sys_note'] = ButtonBarHook::class . '->getButtons';
-- 
GitLab