From 582b23ad633ee9246652b5dfc1277ce2db8ecc3c Mon Sep 17 00:00:00 2001
From: Oliver Bartsch <bo@cedev.de>
Date: Fri, 2 Dec 2022 22:05:07 +0100
Subject: [PATCH] [FEATURE] Introduce TCA type "uuid"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

A new TCA type "uuid" is introduced, which
allows to simplify the configuration when
working with fields, containing a UUID.

The functionality is based on the Uid
symfony component.

FormEngine will automatically create a UUID when
non is defined yet. Same does the DataHandler in
case an invalid UUID is defined, while the field
is defined as "required", which is the default.

The UUID is displayed as a readonly input field.

Using the new TCA type, corresponding database
columns are added automatically.

Resolves: #100171
Releases: main
Change-Id: Ic81d4cc0158e77988e38cdab6ddcc5d42aa47fcd
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76891
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: core-ci <typo3@b13.com>
Tested-by: Frank Nägler <frank.naegler@typo3.com>
Reviewed-by: Frank Nägler <frank.naegler@typo3.com>
---
 .../Classes/Form/Element/UuidElement.php      | 119 +++++++++
 .../Form/FormDataProvider/TcaRecordTitle.php  |   1 +
 .../Classes/Form/FormDataProvider/TcaUuid.php |  49 ++++
 .../backend/Classes/Form/NodeFactory.php      |   1 +
 .../Form/Utility/FormEngineUtility.php        |   1 +
 .../Classes/RecordList/DatabaseRecordList.php |  43 ++--
 .../LiveSearch/DatabaseRecordProvider.php     |   1 +
 .../Language/locallang_copytoclipboard.xlf    |   3 +
 .../Unit/Form/Element/UuidElementTest.php     | 164 +++++++++++++
 .../Form/FormDataProvider/TcaUuidTest.php     | 226 ++++++++++++++++++
 .../core/Classes/DataHandling/DataHandler.php |  28 +++
 .../Classes/DataHandling/TableColumnType.php  |   1 +
 .../Database/Schema/DefaultTcaSchema.php      |  19 ++
 .../SearchTermRestriction.php                 |   1 +
 .../Configuration/DefaultConfiguration.php    |  12 +-
 .../Feature-100171-IntroduceTCATypeUuid.rst   |  65 +++++
 .../Unit/DataHandling/DataHandlerTest.php     |  44 ++++
 typo3/sysext/core/composer.json               |   1 +
 .../Generic/Mapper/DataMapFactoryTest.php     |   1 +
 .../DatabaseIntegrityController.php           |   2 +
 .../Classes/Form/Element/UuidElement.php      |  80 -------
 .../Classes/Hooks/DataHandlerHook.php         |  47 ----
 .../Configuration/TCA/sys_reaction.php        |   3 +-
 typo3/sysext/reactions/composer.json          |   1 -
 typo3/sysext/reactions/ext_localconf.php      |  11 -
 typo3/sysext/reactions/ext_tables.sql         |   1 -
 26 files changed, 760 insertions(+), 165 deletions(-)
 create mode 100644 typo3/sysext/backend/Classes/Form/Element/UuidElement.php
 create mode 100644 typo3/sysext/backend/Classes/Form/FormDataProvider/TcaUuid.php
 create mode 100644 typo3/sysext/backend/Tests/Unit/Form/Element/UuidElementTest.php
 create mode 100644 typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaUuidTest.php
 create mode 100644 typo3/sysext/core/Documentation/Changelog/12.3/Feature-100171-IntroduceTCATypeUuid.rst
 delete mode 100644 typo3/sysext/reactions/Classes/Form/Element/UuidElement.php
 delete mode 100644 typo3/sysext/reactions/Classes/Hooks/DataHandlerHook.php

diff --git a/typo3/sysext/backend/Classes/Form/Element/UuidElement.php b/typo3/sysext/backend/Classes/Form/Element/UuidElement.php
new file mode 100644
index 000000000000..23e29a80f09e
--- /dev/null
+++ b/typo3/sysext/backend/Classes/Form/Element/UuidElement.php
@@ -0,0 +1,119 @@
+<?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\Form\Element;
+
+use Symfony\Component\Uid\Uuid;
+use TYPO3\CMS\Core\Imaging\Icon;
+use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
+use TYPO3\CMS\Core\Utility\StringUtility;
+
+/**
+ * Render a readonly input field, which is filled with a UUID
+ */
+class UuidElement extends AbstractFormElement
+{
+    /**
+     * Default field information enabled for this element.
+     *
+     * @var array
+     */
+    protected $defaultFieldInformation = [
+        'tcaDescription' => [
+            'renderType' => 'tcaDescription',
+        ],
+    ];
+
+    public function render(): array
+    {
+        $resultArray = $this->initializeResultArray();
+        $parameterArray = $this->data['parameterArray'];
+        $itemValue = htmlspecialchars((string)$parameterArray['itemFormElValue'], ENT_QUOTES);
+        $config = $parameterArray['fieldConf']['config'];
+        $itemName = $parameterArray['itemFormElName'];
+        $fieldId = StringUtility::getUniqueId('formengine-uuid-');
+
+        if (!isset($config['required'])) {
+            $config['required'] = true;
+        }
+
+        if ($config['required'] && !Uuid::isValid($itemValue)) {
+            // Note: This can only happen in case the TcaUuid data provider is not executed or a custom
+            // data provider has changed the value afterwards. Since this can only happen in user code,
+            // we throw an exception to inform the administrator about this misconfiguration.
+            throw new \RuntimeException(
+                'Field "' . $this->data['fieldName'] . '" in table "' . $this->data['tableName'] . '" of type "uuid" defines the field to be required but does not contain a valid uuid. Make sure to properly generate a valid uuid value.',
+                1678895476
+            );
+        }
+
+        $width = $this->formMaxWidth(
+            MathUtility::forceIntegerInRange($config['size'] ?? $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth)
+        );
+
+        $attributes = [
+            'id' => $fieldId,
+            'name' => $itemName,
+            'type' => 'text',
+            'readonly' => 'readonly',
+            'class' => 'form-control disabled',
+            'data-formengine-input-name' => $itemName,
+            'data-formengine-validation-rules' => $this->getValidationDataAsJsonString($config),
+        ];
+
+        $uuidElement = '
+            <input value="' . $itemValue . '"
+                ' . GeneralUtility::implodeAttributes($attributes, true) . '
+            />';
+
+        $fieldInformationResult = $this->renderFieldInformation();
+        $fieldInformationHtml = $fieldInformationResult['html'];
+        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
+
+        if (($config['enableCopyToClipboard'] ?? true) !== false) {
+            $uuidElement = '
+                <div class="input-group">
+                    ' . $uuidElement . '
+                    <typo3-copy-to-clipboard text="' . $itemValue . '">
+                        <button type="button" class="btn btn-default" title="' . htmlspecialchars(sprintf($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_copytoclipboard.xlf:copyToClipboard.title'), 'UUID')) . '">
+                            ' . $this->iconFactory->getIcon('actions-clipboard', Icon::SIZE_SMALL) . '
+                        </button>
+                    </typo3-copy-to-clipboard>
+                </div>';
+
+            $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create('@typo3/backend/copy-to-clipboard.js');
+        }
+
+        $html = [];
+        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
+        $html[] =     $fieldInformationHtml;
+        $html[] =     '<div class="form-control-wrap" style="max-width: ' . $width . 'px">';
+        $html[] =         '<div class="form-wizards-wrap">';
+        $html[] =             '<div class="form-wizards-element">';
+        $html[] =                 $uuidElement;
+        $html[] =             '</div>';
+        $html[] =         '</div>';
+        $html[] =     '</div>';
+        $html[] = '</div>';
+
+        $resultArray['html'] = implode(LF, $html);
+
+        return $resultArray;
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php
index 273964dff896..754a9b001802 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaRecordTitle.php
@@ -177,6 +177,7 @@ class TcaRecordTitle implements FormDataProviderInterface
                 break;
             case 'input':
             case 'number':
+            case 'uuid':
                 $recordTitle = $rawValue ?? '';
                 break;
             case 'text':
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaUuid.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaUuid.php
new file mode 100644
index 000000000000..0cbb8b504880
--- /dev/null
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaUuid.php
@@ -0,0 +1,49 @@
+<?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\Form\FormDataProvider;
+
+use Symfony\Component\Uid\Uuid;
+use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
+
+/**
+ * Generates and sets field value for type=uuid
+ */
+class TcaUuid implements FormDataProviderInterface
+{
+    public function addData(array $result): array
+    {
+        foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
+            if (($fieldConfig['config']['type'] ?? '') !== 'uuid') {
+                continue;
+            }
+
+            // Skip if field is already filled with a valid uuid
+            if (Uuid::isValid((string)($result['databaseRow'][$fieldName] ?? ''))) {
+                continue;
+            }
+
+            $result['databaseRow'][$fieldName] = (string)match ((int)($fieldConfig['config']['version'] ?? 0)) {
+                6 => Uuid::v6(),
+                7 => Uuid::v7(),
+                default => Uuid::v4()
+            };
+        }
+
+        return $result;
+    }
+}
diff --git a/typo3/sysext/backend/Classes/Form/NodeFactory.php b/typo3/sysext/backend/Classes/Form/NodeFactory.php
index 069f8f5949a1..eb9364e3c8a7 100644
--- a/typo3/sysext/backend/Classes/Form/NodeFactory.php
+++ b/typo3/sysext/backend/Classes/Form/NodeFactory.php
@@ -108,6 +108,7 @@ class NodeFactory
         'passthrough' => Element\PassThroughElement::class,
         'belayoutwizard' => Element\BackendLayoutWizardElement::class,
         'json' => Element\JsonElement::class,
+        'uuid' => Element\UuidElement::class,
 
         // Default classes to enrich single elements
         'fieldControl' => NodeExpansion\FieldControl::class,
diff --git a/typo3/sysext/backend/Classes/Form/Utility/FormEngineUtility.php b/typo3/sysext/backend/Classes/Form/Utility/FormEngineUtility.php
index fc8b3715fd19..06500e94e373 100644
--- a/typo3/sysext/backend/Classes/Form/Utility/FormEngineUtility.php
+++ b/typo3/sysext/backend/Classes/Form/Utility/FormEngineUtility.php
@@ -51,6 +51,7 @@ class FormEngineUtility
         'password' => ['size', 'readOnly'],
         'datetime' => ['size', 'readOnly'],
         'color' => ['size', 'readOnly'],
+        'uuid' => ['size', 'enableCopyToClipboard'],
         'text' => ['cols', 'rows', 'wrap', 'max', 'readOnly'],
         'json' => ['cols', 'rows', 'readOnly'],
         'check' => ['cols', 'readOnly'],
diff --git a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
index 245a4be3bbc7..ce1be6618e31 100644
--- a/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
+++ b/typo3/sysext/backend/Classes/RecordList/DatabaseRecordList.php
@@ -2389,15 +2389,7 @@ class DatabaseRecordList
                             $expressionBuilder->eq($tablePidField, (int)$currentPid)
                         );
                     }
-                } elseif ($fieldType === 'input'
-                    || $fieldType === 'text'
-                    || $fieldType === 'json'
-                    || $fieldType === 'flex'
-                    || $fieldType === 'email'
-                    || $fieldType === 'link'
-                    || $fieldType === 'slug'
-                    || $fieldType === 'color'
-                ) {
+                } elseif ($this->isTextFieldType($fieldType)) {
                     $constraints[] = $expressionBuilder->like(
                         $fieldName,
                         $queryBuilder->quote('%' . (int)$this->searchString . '%')
@@ -2434,18 +2426,8 @@ class DatabaseRecordList
                         );
                     }
                 }
-                if ($fieldType === 'input'
-                    || $fieldType === 'text'
-                    || $fieldType === 'json'
-                    || $fieldType === 'flex'
-                    || $fieldType === 'email'
-                    || $fieldType === 'link'
-                    || $fieldType === 'slug'
-                    || $fieldType === 'color'
-                ) {
-                    if ($searchConstraint->count() !== 0) {
-                        $constraints[] = $searchConstraint;
-                    }
+                if ($this->isTextFieldType($fieldType) && $searchConstraint->count() !== 0) {
+                    $constraints[] = $searchConstraint;
                 }
             }
         }
@@ -3156,7 +3138,7 @@ class DatabaseRecordList
     }
 
     /**
-     * Add a divider to the secondary cell gorup, if not already present
+     * Add a divider to the secondary cell group, if not already present
      */
     protected function addDividerToCellGroup(array &$cells): void
     {
@@ -3164,4 +3146,21 @@ class DatabaseRecordList
             $this->addActionToCellGroup($cells, '<hr class="dropdown-divider">', 'divider');
         }
     }
+
+    protected function isTextFieldType(string $fieldType): bool
+    {
+        $textFieldTypes = [
+            'input',
+            'text',
+            'json',
+            'flex',
+            'email',
+            'link',
+            'slug',
+            'color',
+            'uuid',
+        ];
+
+        return in_array($fieldType, $textFieldTypes, true);
+    }
 }
diff --git a/typo3/sysext/backend/Classes/Search/LiveSearch/DatabaseRecordProvider.php b/typo3/sysext/backend/Classes/Search/LiveSearch/DatabaseRecordProvider.php
index 0b25202935e5..c5db1b805198 100644
--- a/typo3/sysext/backend/Classes/Search/LiveSearch/DatabaseRecordProvider.php
+++ b/typo3/sysext/backend/Classes/Search/LiveSearch/DatabaseRecordProvider.php
@@ -346,6 +346,7 @@ final class DatabaseRecordProvider implements SearchProviderInterface
             'link',
             'color',
             'slug',
+            'uuid',
         ];
 
         return in_array($fieldType, $searchableFieldTypes, true);
diff --git a/typo3/sysext/backend/Resources/Private/Language/locallang_copytoclipboard.xlf b/typo3/sysext/backend/Resources/Private/Language/locallang_copytoclipboard.xlf
index b26372b9ae12..4c9609bdd1c3 100644
--- a/typo3/sysext/backend/Resources/Private/Language/locallang_copytoclipboard.xlf
+++ b/typo3/sysext/backend/Resources/Private/Language/locallang_copytoclipboard.xlf
@@ -3,6 +3,9 @@
 	<file source-language="en" datatype="plaintext" original="EXT:backend/Resources/Private/Language/locallang_copytoclipboard.xlf" date="2021-06-01T12:43:12Z" product-name="backend">
 		<header/>
 		<body>
+			<trans-unit id="copyToClipboard.title" resname="copyToClipboard.title">
+				<source>Copy %s to clipboard</source>
+			</trans-unit>
 			<trans-unit id="copyToClipboard.success" resname="copyToClipboard.success">
 				<source>Copied to clipboard</source>
 			</trans-unit>
diff --git a/typo3/sysext/backend/Tests/Unit/Form/Element/UuidElementTest.php b/typo3/sysext/backend/Tests/Unit/Form/Element/UuidElementTest.php
new file mode 100644
index 000000000000..f8b7379388e2
--- /dev/null
+++ b/typo3/sysext/backend/Tests/Unit/Form/Element/UuidElementTest.php
@@ -0,0 +1,164 @@
+<?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\Tests\Unit\Form\Element;
+
+use TYPO3\CMS\Backend\Form\Element\UuidElement;
+use TYPO3\CMS\Backend\Form\NodeExpansion\FieldInformation;
+use TYPO3\CMS\Backend\Form\NodeFactory;
+use TYPO3\CMS\Core\Imaging\IconFactory;
+use TYPO3\CMS\Core\Localization\LanguageService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+class UuidElementTest extends UnitTestCase
+{
+    protected bool $resetSingletonInstances = true;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $GLOBALS['LANG'] = $this->createMock(LanguageService::class);
+    }
+
+    /**
+     * @test
+     */
+    public function renderThrowsExceptionOnEmptyElementValue(): void
+    {
+        $data = [
+            'tableName' => 'aTable',
+            'fieldName' => 'identifier',
+            'parameterArray' => [
+                'itemFormElName' => 'identifier',
+                'itemFormElValue' => '',
+                'fieldConf' => [
+                    'config' => [
+                        'type' => 'uuid',
+                        'required' => true,
+                    ],
+                ],
+            ],
+        ];
+
+        GeneralUtility::addInstance(IconFactory::class, $this->createMock(IconFactory::class));
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1678895476);
+
+        (new UuidElement($this->createMock(NodeFactory::class), $data))->render();
+    }
+
+    /**
+     * @test
+     */
+    public function renderThrowsExceptionOnInvalidUuid(): void
+    {
+        $data = [
+            'tableName' => 'aTable',
+            'fieldName' => 'identifier',
+            'parameterArray' => [
+                'itemFormElName' => 'identifier',
+                'itemFormElValue' => '_-invalid-_',
+                'fieldConf' => [
+                    'config' => [
+                        'type' => 'uuid',
+                        'required' => true,
+                    ],
+                ],
+            ],
+        ];
+
+        GeneralUtility::addInstance(IconFactory::class, $this->createMock(IconFactory::class));
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionCode(1678895476);
+
+        (new UuidElement($this->createMock(NodeFactory::class), $data))->render();
+    }
+
+    /**
+     * @test
+     */
+    public function renderReturnsInputElementWithUuidAndCopyToClipboardButton(): void
+    {
+        $uuid = 'b3190536-1431-453e-afbb-25b8c5022513';
+        $data = [
+            'tableName' => 'aTable',
+            'fieldName' => 'identifier',
+            'parameterArray' => [
+                'itemFormElName' => 'identifier',
+                'itemFormElValue' => $uuid,
+                'fieldConf' => [
+                    'config' => [
+                        'type' => 'uuid',
+                    ],
+                ],
+            ],
+        ];
+
+        GeneralUtility::addInstance(IconFactory::class, $this->createMock(IconFactory::class));
+
+        $nodeFactoryMock = $this->createMock(NodeFactory::class);
+        $fieldInformationMock = $this->createMock(FieldInformation::class);
+        $fieldInformationMock->method('render')->willReturn(['html' => '']);
+        $nodeFactoryMock->method('create')->with(self::anything())->willReturn($fieldInformationMock);
+
+        $subject = new UuidElement($nodeFactoryMock, $data);
+        $result = $subject->render();
+
+        self::assertEquals('@typo3/backend/copy-to-clipboard.js', $result['javaScriptModules'][0]->getName());
+        self::assertMatchesRegularExpression('/<typo3-copy-to-clipboard.*text="' . $uuid . '"/s', $result['html']);
+        self::assertMatchesRegularExpression('/<input.*value="' . $uuid . '".*id="formengine-uuid-/s', $result['html']);
+    }
+
+    /**
+     * @test
+     */
+    public function renderReturnsInputElementWithUuidAndWithoutCopyToClipboardButton(): void
+    {
+        $uuid = 'b3190536-1431-453e-afbb-25b8c5022513';
+        $data = [
+            'tableName' => 'aTable',
+            'fieldName' => 'identifier',
+            'parameterArray' => [
+                'itemFormElName' => 'identifier',
+                'itemFormElValue' => $uuid,
+                'fieldConf' => [
+                    'config' => [
+                        'type' => 'uuid',
+                        'enableCopyToClipboard' => false,
+                    ],
+                ],
+            ],
+        ];
+
+        GeneralUtility::addInstance(IconFactory::class, $this->createMock(IconFactory::class));
+
+        $nodeFactoryMock = $this->createMock(NodeFactory::class);
+        $fieldInformationMock = $this->createMock(FieldInformation::class);
+        $fieldInformationMock->method('render')->willReturn(['html' => '']);
+        $nodeFactoryMock->method('create')->with(self::anything())->willReturn($fieldInformationMock);
+
+        $subject = new UuidElement($nodeFactoryMock, $data);
+        $result = $subject->render();
+
+        self::assertEmpty($result['javaScriptModules']);
+        self::assertDoesNotMatchRegularExpression('/<typo3-copy-to-clipboard.*text="' . $uuid . '"/s', $result['html']);
+        self::assertMatchesRegularExpression('/<input.*value="' . $uuid . '".*id="formengine-uuid-/s', $result['html']);
+    }
+}
diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaUuidTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaUuidTest.php
new file mode 100644
index 000000000000..303a93beea9e
--- /dev/null
+++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaUuidTest.php
@@ -0,0 +1,226 @@
+<?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\Tests\Unit\Form\FormDataProvider;
+
+use Symfony\Component\Uid\Uuid;
+use TYPO3\CMS\Backend\Form\FormDataProvider\TcaUuid;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+class TcaUuidTest extends UnitTestCase
+{
+    public function resultArrayDataProvider(): \Generator
+    {
+        yield 'Only handles TCA type "uuid" records' => [
+            [
+                'command' => 'new',
+                'tableName' => 'aTable',
+                'databaseRow' => [
+                    'aField' => '',
+                ],
+                'processedTca' => [
+                    'columns' => [
+                        'aField' => [
+                            'config' => [
+                                'type' => 'input',
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+            '',
+        ];
+        yield 'Does not handle records with valid uuid value' => [
+            [
+                'command' => 'edit',
+                'tableName' => 'aTable',
+                'databaseRow' => [
+                    'aField' => 'b3190536-1431-453e-afbb-25b8c5022513',
+                ],
+                'processedTca' => [
+                    'columns' => [
+                        'aField' => [
+                            'config' => [
+                                'type' => 'uuid',
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+            'b3190536-1431-453e-afbb-25b8c5022513',
+        ];
+        yield 'Does handle records with invalid uuid value' => [
+            [
+                'command' => 'edit',
+                'tableName' => 'aTable',
+                'databaseRow' => [
+                    'aField' => '_-invalid-_',
+                ],
+                'processedTca' => [
+                    'columns' => [
+                        'aField' => [
+                            'config' => [
+                                'type' => 'uuid',
+                            ],
+                        ],
+                    ],
+                ],
+            ],
+            'b3190536-1431-453e-afbb-25b8c5022513',
+        ];
+    }
+
+    /**
+     * @test
+     */
+    public function addDataDoesOnlyHandleTypeUuid(): void
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [
+                'aField' => '',
+            ],
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'input',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        self::assertSame('', (new TcaUuid())->addData($input)['databaseRow']['aField']);
+    }
+
+    /**
+     * @test
+     */
+    public function addDataDoesNotHandleFieldsWithValidUuidValue(): void
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [
+                'aField' => 'b3190536-1431-453e-afbb-25b8c5022513',
+            ],
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'uuid',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        self::assertSame('b3190536-1431-453e-afbb-25b8c5022513', (new TcaUuid())->addData($input)['databaseRow']['aField']);
+    }
+
+    /**
+     * @test
+     */
+    public function addDataCreatesValidUuidValueForInvalidUuid(): void
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [
+                'aField' => '_-invalid-_',
+            ],
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'uuid',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        self::assertFalse(Uuid::isValid($input['databaseRow']['aField']));
+        self::assertTrue(Uuid::isValid((new TcaUuid())->addData($input)['databaseRow']['aField']));
+    }
+
+    /**
+     * @test
+     */
+    public function addDataCreatesValidUuidValueForEmptyField(): void
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [
+                'aField' => '',
+            ],
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'uuid',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        self::assertFalse(Uuid::isValid($input['databaseRow']['aField']));
+        self::assertTrue(Uuid::isValid((new TcaUuid())->addData($input)['databaseRow']['aField']));
+    }
+
+    /**
+     * @test
+     */
+    public function addDataCreatesValidUuidValueWithDefinedVersion(): void
+    {
+        $input = [
+            'command' => 'new',
+            'tableName' => 'aTable',
+            'databaseRow' => [
+                'aField' => '',
+            ],
+            'processedTca' => [
+                'columns' => [
+                    'aField' => [
+                        'config' => [
+                            'type' => 'uuid',
+                        ],
+                    ],
+                ],
+            ],
+        ];
+
+        $input['processedTca']['columns']['aField']['config']['version'] = 6;
+        self::assertEquals(6, (int)(new TcaUuid())->addData($input)['databaseRow']['aField'][14]);
+
+        $input['processedTca']['columns']['aField']['config']['version'] = 7;
+        self::assertEquals(7, (int)(new TcaUuid())->addData($input)['databaseRow']['aField'][14]);
+
+        $input['processedTca']['columns']['aField']['config']['version'] = 4;
+        self::assertEquals(4, (int)(new TcaUuid())->addData($input)['databaseRow']['aField'][14]);
+
+        $input['processedTca']['columns']['aField']['config']['version'] = 12345678; // Defaults to 4
+        self::assertEquals(4, (int)(new TcaUuid())->addData($input)['databaseRow']['aField'][14]);
+
+        unset($input['processedTca']['columns']['aField']['config']['version']); // Defaults to 4
+        self::assertEquals(4, (int)(new TcaUuid())->addData($input)['databaseRow']['aField'][14]);
+    }
+}
diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
index e5e1b0c41498..a151848ae419 100644
--- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php
+++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
@@ -21,6 +21,7 @@ use Doctrine\DBAL\Types\IntegerType;
 use Doctrine\DBAL\Types\JsonType;
 use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerAwareTrait;
+use Symfony\Component\Uid\Uuid;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
@@ -1503,6 +1504,7 @@ class DataHandler implements LoggerAwareInterface
             'text' => $this->checkValueForText($value, $tcaFieldConf, $table, $realPid, $field),
             'group', 'folder', 'select' => $this->checkValueForGroupFolderSelect($res, $value, $tcaFieldConf, $table, $id, $status, $field),
             'json' => $this->checkValueForJson($value, $tcaFieldConf),
+            'uuid' => $this->checkValueForUuid((string)$value, $tcaFieldConf),
             'passthrough', 'imageManipulation', 'user' => ['value' => $value],
             default => [],
         };
@@ -2385,6 +2387,32 @@ class DataHandler implements LoggerAwareInterface
         return $res;
     }
 
+    /**
+     * Evaluate "uuid" type values. Will create a new uuid in case
+     * an invalid uuid is provided and the field is marked as required.
+     *
+     * @param string $value The value to set.
+     * @param array $tcaFieldConf Field configuration from TCA
+     *
+     * @return array $res The result array. The processed value (if any!) is set in the "value" key.
+     */
+    protected function checkValueForUuid(string $value, array $tcaFieldConf): array
+    {
+        if (Uuid::isValid($value)) {
+            return ['value' => $value];
+        }
+
+        if ($tcaFieldConf['required'] ?? true) {
+            return ['value' => (string)match ((int)($tcaFieldConf['version'] ?? 0)) {
+                6 => Uuid::v6(),
+                7 => Uuid::v7(),
+                default => Uuid::v4()
+            }];
+        }
+        // Unset invalid uuid - in case a field value is not required
+        return [];
+    }
+
     /**
      * Applies the filter methods from a column's TCA configuration to a value array.
      *
diff --git a/typo3/sysext/core/Classes/DataHandling/TableColumnType.php b/typo3/sysext/core/Classes/DataHandling/TableColumnType.php
index be263688ba13..a9bc50544221 100644
--- a/typo3/sysext/core/Classes/DataHandling/TableColumnType.php
+++ b/typo3/sysext/core/Classes/DataHandling/TableColumnType.php
@@ -51,6 +51,7 @@ final class TableColumnType extends Enumeration
     public const NUMBER = 'NUMBER';
     public const FILE = 'FILE';
     public const JSON = 'JSON';
+    public const UUID = 'UUID';
 
     /**
      * @param mixed $type
diff --git a/typo3/sysext/core/Classes/Database/Schema/DefaultTcaSchema.php b/typo3/sysext/core/Classes/Database/Schema/DefaultTcaSchema.php
index afb78963f107..98bb4bc45234 100644
--- a/typo3/sysext/core/Classes/Database/Schema/DefaultTcaSchema.php
+++ b/typo3/sysext/core/Classes/Database/Schema/DefaultTcaSchema.php
@@ -521,6 +521,25 @@ class DefaultTcaSchema
                     ]
                 );
             }
+
+            // Add uuid fields for all tables, defining uuid columns (TCA type=uuid)
+            foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
+                if ((string)($fieldConfig['config']['type'] ?? '') !== 'uuid'
+                    || $this->isColumnDefinedForTable($tables, $tableName, $fieldName)
+                ) {
+                    continue;
+                }
+
+                $tables[$tablePosition]->addColumn(
+                    $this->quote($fieldName),
+                    'string',
+                    [
+                        'length' => 36,
+                        'default' => '',
+                        'notnull' => true,
+                    ]
+                );
+            }
         }
 
         return $tables;
diff --git a/typo3/sysext/core/Classes/Resource/Search/QueryRestrictions/SearchTermRestriction.php b/typo3/sysext/core/Classes/Resource/Search/QueryRestrictions/SearchTermRestriction.php
index 8dcc458a1962..fd63a377c841 100644
--- a/typo3/sysext/core/Classes/Resource/Search/QueryRestrictions/SearchTermRestriction.php
+++ b/typo3/sysext/core/Classes/Resource/Search/QueryRestrictions/SearchTermRestriction.php
@@ -113,6 +113,7 @@ class SearchTermRestriction implements QueryRestrictionInterface
                     || $fieldType === 'link'
                     || $fieldType === 'color'
                     || $fieldType === 'input'
+                    || $fieldType === 'uuid'
                 ) {
                     $constraintsForParts[] = $searchConstraint;
                 }
diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php
index ed54d2a618c6..15beb76f3c89 100644
--- a/typo3/sysext/core/Configuration/DefaultConfiguration.php
+++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php
@@ -604,10 +604,15 @@ return [
                             \TYPO3\CMS\Backend\Form\FormDataProvider\TcaText::class,
                         ],
                     ],
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaUuid::class => [
+                        'depends' => [
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaJson::class,
+                        ],
+                    ],
                     \TYPO3\CMS\Backend\Form\FormDataProvider\TcaRadioItems::class => [
                         'depends' => [
                             \TYPO3\CMS\Backend\Form\FormDataProvider\InitializeProcessedTca::class,
-                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaJson::class,
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaUuid::class,
                         ],
                     ],
                     \TYPO3\CMS\Backend\Form\FormDataProvider\TcaCheckboxItems::class => [
@@ -854,6 +859,11 @@ return [
                             \TYPO3\CMS\Backend\Form\FormDataProvider\TcaText::class,
                         ],
                     ],
+                    \TYPO3\CMS\Backend\Form\FormDataProvider\TcaUuid::class => [
+                        'depends' => [
+                            \TYPO3\CMS\Backend\Form\FormDataProvider\TcaJson::class,
+                        ],
+                    ],
                     \TYPO3\CMS\Backend\Form\FormDataProvider\TcaRadioItems::class => [
                         'depends' => [
                             \TYPO3\CMS\Backend\Form\FormDataProvider\SiteResolving::class,
diff --git a/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100171-IntroduceTCATypeUuid.rst b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100171-IntroduceTCATypeUuid.rst
new file mode 100644
index 000000000000..15c6707fd307
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/12.3/Feature-100171-IntroduceTCATypeUuid.rst
@@ -0,0 +1,65 @@
+.. include:: /Includes.rst.txt
+
+.. _feature-100171-1678869689:
+
+==========================================
+Feature: #100171 - Introduce TCA type uuid
+==========================================
+
+See :issue:`100171`
+
+Description
+===========
+
+In our effort of introducing dedicated TCA types for special use cases,
+a new TCA field type called :php:`uuid` has been added to TYPO3 Core.
+Its main purpose is to simplify the TCA configuration when working with
+fields, containing a UUID.
+
+The TCA type :php:`uuid` features the following column configuration:
+
+- :php:`enableCopyToClipboard`
+- :php:`fieldInformation`
+- :php:`required`: Defaults to :php:`true`
+- :php:`size`
+- :php:`version`
+
+.. note::
+
+    In case :php:`enableCopyToClipboard` is set to :php:`true`, which is the
+    default, a button is rendered next to the input field, which allows to copy
+    the UUID to the operating system's clipboard.
+
+.. note::
+
+    The :php:`version` option defines the uuid version to be used. Allowed
+    values are `4`, `6` or `7`. The default is `4`. For more information
+    about the different versions, have a look at the corresponding
+    `symfony documentation`_.
+
+The following column configuration can be overwritten by Page TSconfig:
+
+- :typoscript:`size`
+- :typoscript:`enableCopyToClipboard`
+
+An example configuration looks like the following:
+
+.. code-block:: php
+
+    'identifier' => [
+        'label' => 'My record identifier',
+        'config' => [
+            'type' => 'uuid',
+            'version' => 6,
+        ],
+    ],
+
+Impact
+======
+
+It's now possible to use a dedicated TCA type for rendering of a UUID field.
+Using the new TCA type, corresponding database columns are added automatically.
+
+.. _symfony documentation: https://symfony.com/doc/current/components/uid.html#uuids
+
+.. index:: Backend, TCA, ext:backend
diff --git a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
index 734abd345c94..40bba7ee3113 100644
--- a/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
+++ b/typo3/sysext/core/Tests/Unit/DataHandling/DataHandlerTest.php
@@ -18,6 +18,7 @@ declare(strict_types=1);
 namespace TYPO3\CMS\Core\Tests\Unit\DataHandling;
 
 use PHPUnit\Framework\MockObject\MockObject;
+use Symfony\Component\Uid\Uuid;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\NullFrontend;
@@ -1095,6 +1096,49 @@ class DataHandlerTest extends UnitTestCase
         );
     }
 
+    /**
+     * @test
+     */
+    public function checkValueForUuidReturnsValidUuidUnmodified(): void
+    {
+        self::assertEquals(
+            'b3190536-1431-453e-afbb-25b8c5022513',
+            Uuid::isValid($this->subject->_call('checkValueForUuid', 'b3190536-1431-453e-afbb-25b8c5022513', [])['value'])
+        );
+    }
+
+    /**
+     * @test
+     */
+    public function checkValueForUuidCreatesValidUuidValueForReqiredFieldsWithInvalidUuidGiven(): void
+    {
+        self::assertTrue(Uuid::isValid($this->subject->_call('checkValueForUuid', '', [])['value']));
+        self::assertTrue(Uuid::isValid($this->subject->_call('checkValueForUuid', '-_invalid_-', [])['value']));
+    }
+
+    /**
+     * @test
+     */
+    public function checkValueForUuidDiscardsInvalidUuidIfFieldIsNotRequired(): void
+    {
+        self::assertEmpty($this->subject->_call('checkValueForUuid', '', ['required' => false]));
+        self::assertEmpty($this->subject->_call('checkValueForUuid', '-_invalid_-', ['required' => false]));
+    }
+
+    /**
+     * @test
+     */
+    public function checkValueForUuidCreatesValidUuidValueWithDefinedVersion(): void
+    {
+        self::assertEquals(6, (int)$this->subject->_call('checkValueForUuid', '', ['version' => 6])['value'][14]);
+        self::assertEquals(7, (int)$this->subject->_call('checkValueForUuid', '', ['version' => 7])['value'][14]);
+        self::assertEquals(4, (int)$this->subject->_call('checkValueForUuid', '', ['version' => 4])['value'][14]);
+        // Defaults to 4
+        self::assertEquals(4, (int)$this->subject->_call('checkValueForUuid', '', ['version' => 123456678])['value'][14]);
+        // Defaults to 4
+        self::assertEquals(4, (int)$this->subject->_call('checkValueForUuid', '', [])['value'][14]);
+    }
+
     /**
      * @test
      * @dataProvider referenceValuesAreCastedDataProvider
diff --git a/typo3/sysext/core/composer.json b/typo3/sysext/core/composer.json
index 6127790262e7..868bbd292b0a 100644
--- a/typo3/sysext/core/composer.json
+++ b/typo3/sysext/core/composer.json
@@ -68,6 +68,7 @@
 		"symfony/options-resolver": "^6.2",
 		"symfony/rate-limiter": "^6.2",
 		"symfony/routing": "^6.2",
+		"symfony/uid": "^6.2",
 		"symfony/yaml": "^6.2",
 		"typo3/class-alias-loader": "^1.1.4",
 		"typo3/cms-cli": "^3.1",
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapFactoryTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapFactoryTest.php
index a9d19203f5f8..27f24a5244d8 100644
--- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapFactoryTest.php
+++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapFactoryTest.php
@@ -464,6 +464,7 @@ class DataMapFactoryTest extends UnitTestCase
             [['type' => 'number'], TableColumnType::NUMBER],
             [['type' => 'file'], TableColumnType::FILE],
             [['type' => 'json'], TableColumnType::JSON],
+            [['type' => 'uuid'], TableColumnType::UUID],
         ];
     }
 
diff --git a/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php b/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php
index cc0f5a57e211..fe1014e76377 100644
--- a/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php
+++ b/typo3/sysext/lowlevel/Classes/Controller/DatabaseIntegrityController.php
@@ -922,6 +922,7 @@ class DatabaseIntegrityController
                     case 'password':
                     case 'color':
                     case 'json':
+                    case 'uuid':
                     default:
                         $fields['type'] = 'text';
                 }
@@ -2257,6 +2258,7 @@ class DatabaseIntegrityController
                         case 'password':
                         case 'color':
                         case 'json':
+                        case 'uuid':
                         default:
                             $this->fields[$fieldName]['type'] = 'text';
                     }
diff --git a/typo3/sysext/reactions/Classes/Form/Element/UuidElement.php b/typo3/sysext/reactions/Classes/Form/Element/UuidElement.php
deleted file mode 100644
index f9298e839bc0..000000000000
--- a/typo3/sysext/reactions/Classes/Form/Element/UuidElement.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?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\Reactions\Form\Element;
-
-use Symfony\Component\Uid\Uuid;
-use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\StringUtility;
-
-/**
- * Creates a readonly input element with a UUID.
- *
- * This is rendered for config type=user, renderType=uuid
- *
- * @internal This is a specific hook implementation and is not considered part of the Public TYPO3 API.
- */
-class UuidElement extends AbstractFormElement
-{
-    /**
-     * Default field information enabled for this element.
-     *
-     * @var array
-     */
-    protected $defaultFieldInformation = [
-        'tcaDescription' => [
-            'renderType' => 'tcaDescription',
-        ],
-    ];
-
-    public function render()
-    {
-        $resultArray = $this->initializeResultArray();
-        $parameterArray = $this->data['parameterArray'];
-        $itemValue = $parameterArray['itemFormElValue'] ?: (string)Uuid::v4();
-        $fieldId = StringUtility::getUniqueId('formengine-input-');
-
-        $attributes = [
-            'id' => $fieldId,
-            'name' => htmlspecialchars($parameterArray['itemFormElName']),
-            'size' => 40,
-            'class' => 'form-control',
-            'data-formengine-input-name' => htmlspecialchars($parameterArray['itemFormElName']),
-        ];
-
-        $fieldInformationResult = $this->renderFieldInformation();
-        $fieldInformationHtml = $fieldInformationResult['html'];
-        $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
-
-        $html = [];
-        $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
-        $html[] =     $fieldInformationHtml;
-        $html[] =     '<div class="form-control-wrap" style="max-width: ' . $this->formMaxWidth($this->defaultInputWidth) . 'px">';
-        $html[] =         '<div class="form-wizards-wrap">';
-        $html[] =             '<div class="form-wizards-element">';
-        $html[] =                 '<input type="text" readonly="readonly" disabled="disabled" value="' . htmlspecialchars($itemValue, ENT_QUOTES) . '" ';
-        $html[] =                     GeneralUtility::implodeAttributes($attributes, true);
-        $html[] =                 '/>';
-        $html[] =             '</div>';
-        $html[] =         '</div>';
-        $html[] =     '</div>';
-        $html[] = '</div>';
-        $resultArray['html'] = implode(LF, $html);
-        return $resultArray;
-    }
-}
diff --git a/typo3/sysext/reactions/Classes/Hooks/DataHandlerHook.php b/typo3/sysext/reactions/Classes/Hooks/DataHandlerHook.php
deleted file mode 100644
index 46abba94c484..000000000000
--- a/typo3/sysext/reactions/Classes/Hooks/DataHandlerHook.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?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\Reactions\Hooks;
-
-use Symfony\Component\Uid\Uuid;
-use TYPO3\CMS\Core\DataHandling\DataHandler;
-
-/**
- * @internal This is a specific hook implementation and is not considered part of the Public TYPO3 API.
- */
-class DataHandlerHook
-{
-    public function processDatamap_postProcessFieldArray($status, $table, $id, array &$fieldArray, DataHandler $dataHandler): void
-    {
-        // Only consider reactions
-        if ($table !== 'sys_reaction') {
-            return;
-        }
-        // Only consider new reactions
-        if ($status !== 'new') {
-            return;
-        }
-        // Create a UUID for a new reaction if non is present in the field array
-        if (!isset($fieldArray['identifier'])) {
-            $fieldArray['identifier'] = (string)Uuid::v4();
-        }
-        // Create a valid UUID for a new reaction if given identifier is invalid
-        if (!Uuid::isValid($fieldArray['identifier'])) {
-            $fieldArray['identifier'] = (string)Uuid::v4();
-        }
-    }
-}
diff --git a/typo3/sysext/reactions/Configuration/TCA/sys_reaction.php b/typo3/sysext/reactions/Configuration/TCA/sys_reaction.php
index 7c3b5efd8817..04fbc47c4fdb 100644
--- a/typo3/sysext/reactions/Configuration/TCA/sys_reaction.php
+++ b/typo3/sysext/reactions/Configuration/TCA/sys_reaction.php
@@ -80,8 +80,7 @@ return [
             'label' => 'LLL:EXT:reactions/Resources/Private/Language/locallang_db.xlf:sys_reaction.identifier',
             'description' => 'LLL:EXT:reactions/Resources/Private/Language/locallang_db.xlf:sys_reaction.identifier.description',
             'config' => [
-                'type' => 'user',
-                'renderType' => 'uuid',
+                'type' => 'uuid',
             ],
         ],
         'secret' => [
diff --git a/typo3/sysext/reactions/composer.json b/typo3/sysext/reactions/composer.json
index fcd929e598db..70f8b320a88c 100644
--- a/typo3/sysext/reactions/composer.json
+++ b/typo3/sysext/reactions/composer.json
@@ -19,7 +19,6 @@
 		"sort-packages": true
 	},
 	"require": {
-		"symfony/uid": "^6.2",
 		"typo3/cms-core": "12.3.*@dev"
 	},
 	"suggest": {
diff --git a/typo3/sysext/reactions/ext_localconf.php b/typo3/sysext/reactions/ext_localconf.php
index b25163e51c8b..72c7e317f5dc 100644
--- a/typo3/sysext/reactions/ext_localconf.php
+++ b/typo3/sysext/reactions/ext_localconf.php
@@ -3,8 +3,6 @@
 declare(strict_types=1);
 
 use TYPO3\CMS\Reactions\Form\Element\FieldMapElement;
-use TYPO3\CMS\Reactions\Form\Element\UuidElement;
-use TYPO3\CMS\Reactions\Hooks\DataHandlerHook;
 
 defined('TYPO3') or die();
 
@@ -13,12 +11,3 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1660911089] = [
     'priority' => 40,
     'class' => FieldMapElement::class,
 ];
-
-// @todo This should be a dedicated TCA type instead
-$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1660911009] = [
-    'nodeName' => 'uuid',
-    'priority' => 40,
-    'class' => UuidElement::class,
-];
-
-$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = DataHandlerHook::class;
diff --git a/typo3/sysext/reactions/ext_tables.sql b/typo3/sysext/reactions/ext_tables.sql
index 87fc55ea9df2..3de1a2af69ea 100644
--- a/typo3/sysext/reactions/ext_tables.sql
+++ b/typo3/sysext/reactions/ext_tables.sql
@@ -4,7 +4,6 @@
 CREATE TABLE sys_reaction (
 	name varchar(100) DEFAULT '' NOT NULL,
 	reaction_type varchar(255) DEFAULT '' NOT NULL,
-	identifier varchar(36) DEFAULT '' NOT NULL,
 	secret varchar(255) DEFAULT '' NOT NULL,
 	impersonate_user int(11) unsigned DEFAULT '0' NOT NULL,
 	table_name varchar(255) DEFAULT '' NOT NULL,
-- 
GitLab