From 564b3f6ed206c10a74ee7b091bbd438c4b8b5b93 Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Tue, 4 Oct 2022 16:47:20 +0200
Subject: [PATCH] [FEATURE] Introduce PSR-14 event to modify form data for edit
 file form
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This introduces a new PSR-14 event, enabling
extension authors to modify the form data, used
by FormEngine to generate the edit file form in
the filelist module.

TYPO3 internally makes use of this event to
initialize t3editor with the suitable format options
for the file to be edited. This functionality was
previously done in a hook (#98494), which
however did no longer work due to the removal
of said hook in #97452.

Effectively this means, the new Event is an
improved replacement for the removed hook,
since the Event now provides the whole form
data array as well as the resolved FileInterface
and the current PSR-7 Request.

Resolves: #98521
Related: #98494
Related: #97452
Releases: main
Change-Id: I5de9f8ea72fcb4296f6539449f991347cbef17b6
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75982
Tested-by: core-ci <typo3@b13.com>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
---
 composer.json                                 |  1 +
 ...g-97452-RemovedEditFileControllerHooks.rst | 16 +++--
 ...14EventToModifyFormDataForEditFileForm.rst | 71 +++++++++++++++++++
 .../Controller/File/EditFileController.php    |  7 ++
 .../Event/ModifyEditFileFormDataEvent.php     | 55 ++++++++++++++
 .../Event/ModifyEditFileFormDataEventTest.php | 68 ++++++++++++++++++
 .../Php/ArrayDimensionMatcher.php             |  1 +
 .../InitializeT3editorInEditFileForm.php      | 54 ++++++++++++++
 .../t3editor/Configuration/Services.yaml      |  5 ++
 9 files changed, 271 insertions(+), 7 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.1/Feature-98521-PSR-14EventToModifyFormDataForEditFileForm.rst
 create mode 100644 typo3/sysext/filelist/Classes/Event/ModifyEditFileFormDataEvent.php
 create mode 100644 typo3/sysext/filelist/Tests/Unit/Event/ModifyEditFileFormDataEventTest.php
 create mode 100644 typo3/sysext/t3editor/Classes/EventListener/InitializeT3editorInEditFileForm.php

diff --git a/composer.json b/composer.json
index 2341d0f7a550..6c8ae71f2071 100644
--- a/composer.json
+++ b/composer.json
@@ -269,6 +269,7 @@
 			"TYPO3\\CMS\\Extbase\\Tests\\": "typo3/sysext/extbase/Tests/",
 			"TYPO3\\CMS\\Extensionmanager\\Tests\\": "typo3/sysext/extensionmanager/Tests/",
 			"TYPO3\\CMS\\FrontendLogin\\Tests\\": "typo3/sysext/felogin/Tests/",
+			"TYPO3\\CMS\\Filelist\\Tests\\": "typo3/sysext/filelist/Tests/",
 			"TYPO3\\CMS\\Filemetadata\\Tests\\": "typo3/sysext/filemetadata/Tests/",
 			"TYPO3\\CMS\\Fluid\\Tests\\": "typo3/sysext/fluid/Tests/",
 			"TYPO3\\CMS\\FluidStyledContent\\Tests\\": "typo3/sysext/fluid_styled_content/Tests/",
diff --git a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97452-RemovedEditFileControllerHooks.rst b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97452-RemovedEditFileControllerHooks.rst
index 40e6acc4bfa4..4807a0ff0457 100644
--- a/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97452-RemovedEditFileControllerHooks.rst
+++ b/typo3/sysext/core/Documentation/Changelog/12.0/Breaking-97452-RemovedEditFileControllerHooks.rst
@@ -11,9 +11,11 @@ See :issue:`97452`
 Description
 ===========
 
-The hooks :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook']` and
-:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook']` have been removed.
-The same behavior may be achieved using template overrides.
+The hooks :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook']`
+and :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['postOutputProcessingHook']`
+have been removed, since adjusting the generated content can be achieved using template overrides
+and modifing the form data, used to generate the edit file form, can be done
+using the PSR-14 :php:`TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent`.
 
 Impact
 ======
@@ -24,14 +26,14 @@ The extension scanner will report possible usages.
 Affected Installations
 ======================
 
-All TYPO3 installations using these hook in custom extension code. This is pretty
-unlikely, since both hooks were of limited use.
+All TYPO3 installations using these hook in custom extension code. This is
+pretty unlikely, since both hooks were of limited use.
 
 Migration
 =========
 
-The content preparation allowed by :php:`preOutputProcessingHook` can be achieved with
-:ref:`FormEngine data providers <t3coreapi:FormEngine-DataCompiling>`.
+The form data modification, allowed by :php:`preOutputProcessingHook`, can be
+achieved with the new :doc:`PSR-14 ModifyEditFileFormDataEvent <Feature-98521-PSR-14EventToModifyFormDataForEditFileForm>`.
 
 The content manipulation :php:`postOutputProcessingHook` hook can be substituted with a template override
 as outlined in :doc:`this changelog entry <Feature-96812-OverrideBackendTemplatesWithTSconfig>`.
diff --git a/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98521-PSR-14EventToModifyFormDataForEditFileForm.rst b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98521-PSR-14EventToModifyFormDataForEditFileForm.rst
new file mode 100644
index 000000000000..8115f8b5a7ea
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.1/Feature-98521-PSR-14EventToModifyFormDataForEditFileForm.rst
@@ -0,0 +1,71 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-98521-1664890745:
+
+=====================================================================
+Feature: #98521 - PSR-14 event to modify form data for edit file form
+=====================================================================
+
+See :issue:`98521`
+
+Description
+===========
+
+A new PSR-14 :php:`TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent`
+has been added, which allows to modify the form data, used to render the
+file edit form in the :guilabel:`File => Filelist` module using
+:ref:`FormEngine data compiling <t3coreapi:FormEngine-DataCompiling>`.
+
+The new event can be used as an improved alternative for the removed
+:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/file_edit.php']['preOutputProcessingHook']`
+hook.
+
+The event features the following methods:
+
+- :php:`getFormData()`: Returns the current :php:`$formData` array
+- :php:`setFormData()`: Sets the :php:`$formData` array
+- :php:`getFile()`: Returns the corresponding :php:`FileInterface`
+- :php:`getRequest()`: Returns the full PSR-7 :php:`ServerRequestInterface`
+
+Registration of the event in your extensions' :file:`Services.yaml`:
+
+..  code-block:: yaml
+
+    MyVendor\MyPackage\EventListener\ModifyEditFileFormDataEventListener:
+      tags:
+        - name: event.listener
+          identifier: 'my-package/modify-edit-file-form-data-event-listener'
+
+The corresponding event listener class:
+
+..  code-block:: php
+
+    use TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent;
+
+    final class ModifyEditFileFormDataEventListener
+    {
+        public function __invoke(ModifyEditFileFormDataEvent $event): void
+        {
+            // Get current form data
+            $formData = $event->getFormData();
+
+            // Change TCA "renderType" based on the file extension
+            $fileExtension = $event->getFile()->getExtension();
+            if ($fileExtension === 'ts') {
+                $formData['processedTca']['columns']['data']['config']['renderType'] = 'tsRenderer';
+            }
+
+            // Set updated form data
+            $event->setFormData($formData);
+        }
+    }
+
+Impact
+======
+
+It's now possible to modify the whole :php:`$formData` array, used to generate
+the edit file form in the :guilabel:`File => Filelist` module, while having the
+resolved :php:`FileInterface` and the current PSR-7 :php:`ServerRequestInterface`
+available.
+
+.. index:: Backend, PHP-API, ext:filelist
diff --git a/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php b/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
index 6b91bd115600..1f1da1952283 100644
--- a/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
+++ b/typo3/sysext/filelist/Classes/Controller/File/EditFileController.php
@@ -17,6 +17,7 @@ declare(strict_types=1);
 
 namespace TYPO3\CMS\Filelist\Controller\File;
 
+use Psr\EventDispatcher\EventDispatcherInterface;
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Message\StreamFactoryInterface;
@@ -37,6 +38,7 @@ use TYPO3\CMS\Core\Resource\FileInterface;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
 use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent;
 
 /**
  * Edit text files via FormEngine. Reachable via FileList module "Edit content".
@@ -98,6 +100,7 @@ class EditFileController
         protected readonly ModuleTemplateFactory $moduleTemplateFactory,
         protected readonly ResponseFactory $responseFactory,
         protected readonly StreamFactoryInterface $streamFactory,
+        protected readonly EventDispatcherInterface $eventDispatcher,
     ) {
     }
 
@@ -146,6 +149,10 @@ class EditFileController
         $formData['databaseRow']['redirect'] = (string)$this->uriBuilder->buildUriFromRoute('file_edit', ['target' => $combinedIdentifier]);
         $formData['processedTca']['columns']['data'] = $dataColumnDefinition;
 
+        $formData = $this->eventDispatcher->dispatch(
+            new ModifyEditFileFormDataEvent($formData, $file, $request)
+        )->getFormData();
+
         $resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
         $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
         $formResultCompiler->mergeResult($resultArray);
diff --git a/typo3/sysext/filelist/Classes/Event/ModifyEditFileFormDataEvent.php b/typo3/sysext/filelist/Classes/Event/ModifyEditFileFormDataEvent.php
new file mode 100644
index 000000000000..bf345e1e7ccb
--- /dev/null
+++ b/typo3/sysext/filelist/Classes/Event/ModifyEditFileFormDataEvent.php
@@ -0,0 +1,55 @@
+<?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\Filelist\Event;
+
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Resource\FileInterface;
+
+/**
+ * Listeners to this event are be able to modify the form data,
+ * used to render the edit file form in the filelist module.
+ */
+final class ModifyEditFileFormDataEvent
+{
+    public function __construct(
+        private array $formData,
+        private readonly FileInterface $file,
+        private readonly ServerRequestInterface $request
+    ) {
+    }
+
+    public function getFormData(): array
+    {
+        return $this->formData;
+    }
+
+    public function setFormData(array $formData): void
+    {
+        $this->formData = $formData;
+    }
+
+    public function getFile(): FileInterface
+    {
+        return $this->file;
+    }
+
+    public function getRequest(): ServerRequestInterface
+    {
+        return $this->request;
+    }
+}
diff --git a/typo3/sysext/filelist/Tests/Unit/Event/ModifyEditFileFormDataEventTest.php b/typo3/sysext/filelist/Tests/Unit/Event/ModifyEditFileFormDataEventTest.php
new file mode 100644
index 000000000000..ee77f69c68ba
--- /dev/null
+++ b/typo3/sysext/filelist/Tests/Unit/Event/ModifyEditFileFormDataEventTest.php
@@ -0,0 +1,68 @@
+<?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\Filelist\Tests\Unit\Event;
+
+use Prophecy\PhpUnit\ProphecyTrait;
+use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Http\Uri;
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Resource\ResourceStorage;
+use TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+class ModifyEditFileFormDataEventTest extends UnitTestCase
+{
+    use ProphecyTrait;
+
+    /**
+     * @test
+     */
+    public function gettersReturnInitializedObjects(): void
+    {
+        $formData = [
+            'databaseRow' => [
+                'uid' => 123,
+            ],
+            'tableName' => 'editfile',
+            'processedTca' => [
+                'columns' => [
+                    'data' => [
+                        'config' => [
+                            'type' => 'someType',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+        $resourceStorageProphecy = $this->prophesize(ResourceStorage::class);
+        $file = new File([], $resourceStorageProphecy->reveal());
+        $request = new ServerRequest(new Uri('https://example.com'));
+
+        $event = new ModifyEditFileFormDataEvent($formData, $file, $request);
+
+        self::assertEquals($formData, $event->getFormData());
+        self::assertEquals($file, $event->getFile());
+        self::assertEquals($request, $event->getRequest());
+
+        $modifyFormData = $event->getFormData();
+        $modifyFormData['processedTca']['columns']['data']['config']['type'] = 'newType';
+        $event->setFormData($modifyFormData);
+
+        self::assertEquals($modifyFormData, $event->getFormData());
+    }
+}
diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
index 5b6eb4eaaaf5..1b52919921f6 100644
--- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
+++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ArrayDimensionMatcher.php
@@ -904,6 +904,7 @@ return [
     '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'typo3/file_edit.php\'][\'preOutputProcessingHook\']' => [
         'restFiles' => [
             'Breaking-97452-RemovedEditFileControllerHooks.rst',
+            'Feature-98521-PSR-14EventToModifyFormDataForEditFileForm.rst',
         ],
     ],
     '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'typo3/file_edit.php\'][\'postOutputProcessingHook\']' => [
diff --git a/typo3/sysext/t3editor/Classes/EventListener/InitializeT3editorInEditFileForm.php b/typo3/sysext/t3editor/Classes/EventListener/InitializeT3editorInEditFileForm.php
new file mode 100644
index 000000000000..96ce9ec7cd40
--- /dev/null
+++ b/typo3/sysext/t3editor/Classes/EventListener/InitializeT3editorInEditFileForm.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\T3editor\EventListener;
+
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Filelist\Event\ModifyEditFileFormDataEvent;
+use TYPO3\CMS\T3editor\Exception\InvalidModeException;
+use TYPO3\CMS\T3editor\Registry\ModeRegistry;
+use TYPO3\CMS\T3editor\T3editor;
+
+/**
+ * Listener which modifies the form data to initialize t3editor with
+ * the resolved format option (based on the file extension).
+ */
+final class InitializeT3editorInEditFileForm
+{
+    public function __construct(private readonly ModeRegistry $modeRegistry)
+    {
+    }
+
+    public function __invoke(ModifyEditFileFormDataEvent $event): void
+    {
+        // Compile and register t3editor configuration
+        GeneralUtility::makeInstance(T3editor::class)->registerConfiguration();
+
+        $fileExtension = $event->getFile()->getExtension();
+
+        try {
+            $mode = $this->modeRegistry->getByFileExtension($fileExtension);
+        } catch (InvalidModeException $e) {
+            $mode = $this->modeRegistry->getDefaultMode();
+        }
+
+        $formData = $event->getFormData();
+        $formData['processedTca']['columns']['data']['config']['renderType'] = 't3editor';
+        $formData['processedTca']['columns']['data']['config']['format'] = $mode->getFormatCode();
+        $event->setFormData($formData);
+    }
+}
diff --git a/typo3/sysext/t3editor/Configuration/Services.yaml b/typo3/sysext/t3editor/Configuration/Services.yaml
index f9134d1f5234..8ed6d26dbb82 100644
--- a/typo3/sysext/t3editor/Configuration/Services.yaml
+++ b/typo3/sysext/t3editor/Configuration/Services.yaml
@@ -6,3 +6,8 @@ services:
 
   TYPO3\CMS\T3editor\:
     resource: '../Classes/*'
+
+  TYPO3\CMS\T3editor\EventListener\InitializeT3editorInEditFileForm:
+    tags:
+      - name: event.listener
+        identifier: 'typo3-t3editor/initialize-t3editor-in-edit-file-form'
-- 
GitLab