From 8fad94bc5af2fb3f085c6e0d16c790cac7c3727b Mon Sep 17 00:00:00 2001
From: Frank Naegler <frank.naegler@typo3.org>
Date: Thu, 10 Nov 2016 11:11:46 +0100
Subject: [PATCH] [BUGFIX] selectTree pageTsConfig addItems

Adding items for type selectTree with pageTsConfig does not
work as documented. The TCA tree needs multi-root handling
to handle that. Additionally, some slight changes in the order
of elements when items are calculated are needed.

Resolves: #78628
Releases: master
Change-Id: Iecf0225c0eeaab8ea661997cad26e68d255d8460
Reviewed-on: https://review.typo3.org/50565
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
---
 .../Form/Element/SelectTreeElement.php        |  5 +-
 .../FormDataProvider/AbstractItemProvider.php | 24 ++++++++-
 .../FormDataProvider/TcaSelectTreeItems.php   |  7 +--
 .../FormEngine/Element/SelectTree.js          |  5 ++
 .../JavaScript/FormEngine/Element/SvgTree.js  | 42 ++++++++++++++--
 .../Classes/Category/CategoryRegistry.php     |  2 +-
 ...caTreeTreePageTsConfigAddItemsIconPath.rst | 50 +++++++++++++++++++
 .../frontend/Configuration/TCA/tt_content.php |  2 +-
 8 files changed, 125 insertions(+), 12 deletions(-)
 create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-78628-TcaTreeTreePageTsConfigAddItemsIconPath.rst

diff --git a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php
index b23d40b91f36..a8725b9c9b04 100644
--- a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php
+++ b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php
@@ -69,12 +69,11 @@ class SelectTreeElement extends AbstractFormElement
         $expanded = !empty($appearance['expandAll']);
         $showHeader = !empty($appearance['showHeader']);
         if (isset($config['size']) && (int)$config['size'] > 0) {
-            $height = min(max(count($config['items']), $this->minItemsToShow), (int)$config['size']);
+            $height = max($this->minItemsToShow, (int)$config['size']);
         } else {
             $height = $this->itemsToShow;
         }
         $heightInPx = $height * $this->itemHeight;
-
         $treeWrapperId = 'tree_' . $formElementId;
 
         $flexFormFieldName = !empty($parameterArray['fieldConf']['flexFormFieldName']) ? htmlspecialchars($parameterArray['fieldConf']['flexFormFieldName']) : '';
@@ -95,7 +94,7 @@ class SelectTreeElement extends AbstractFormElement
         $html[] = '           data-tree-show-toolbar="' . $showHeader . '"';
         $html[] = '           name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"';
         $html[] = '           id="treeinput' . $formElementId . '"';
-        $html[] = '           value="' . htmlspecialchars(implode(',', $config['treeData']['selectedNodes'])) . '"';
+        $html[] = '           value=""';
         $html[] = '    />';
         $html[] = '</div>';
         $html[] = '<div id="' . $treeWrapperId . '" class="svg-tree-wrapper" style="height: ' . $heightInPx . 'px;"></div>';
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php
index 26f99f69e953..f9862e447df1 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/AbstractItemProvider.php
@@ -24,6 +24,7 @@ use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Query\QueryHelper;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\RelationHandler;
+use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Imaging\IconRegistry;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
@@ -117,6 +118,9 @@ abstract class AbstractItemProvider
     protected function addItemsFromPageTsConfig(array $result, $fieldName, array $items)
     {
         $table = $result['tableName'];
+        $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
+        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
+
         if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
             && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
         ) {
@@ -132,7 +136,25 @@ abstract class AbstractItemProvider
                     && is_array($addItemsArray[$value . '.'])
                     && !empty($addItemsArray[$value . '.']['icon'])
                 ) {
-                    $icon = $addItemsArray[$value . '.']['icon'];
+                    $iconIdentifier = $addItemsArray[$value . '.']['icon'];
+                    if (!$iconRegistry->isRegistered($iconIdentifier)) {
+                        GeneralUtility::deprecationLog(
+                            'Using a file path for icon in pageTsConfig addItems is deprecated.' .
+                            'Use a registered iconIdentifier instead'
+                        );
+                        $iconPath = GeneralUtility::getFileAbsFileName($iconIdentifier);
+                        if ($iconPath !== '') {
+                            $iconIdentifier = md5($iconPath);
+                            $iconRegistry->registerIcon(
+                                $iconIdentifier,
+                                $iconRegistry->detectIconProvider($iconPath),
+                                [
+                                    'source' => $iconPath
+                                ]
+                            );
+                        }
+                    }
+                    $icon = $iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL)->getMarkup('inline');
                 }
                 $items[] = [$label, $value, $icon];
             }
diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php
index 50b36fe0dcbd..eede7112f17c 100644
--- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php
+++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php
@@ -49,15 +49,16 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
             $fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName);
             $fieldConfig['config']['maxitems'] = $this->sanitizeMaxItems($fieldConfig['config']['maxitems']);
 
+            $pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []);
             $fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']);
             $fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']);
-            $staticItems = $fieldConfig['config']['items'];
+            $staticItems = $fieldConfig['config']['items'] + $pageTsConfigAddItems;
 
             $fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']);
             $dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems);
 
             $fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
-            $fieldConfig['config']['items'] = $this->addItemsFromPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
+            $fieldConfig['config']['items'] = $pageTsConfigAddItems + $fieldConfig['config']['items'];
             $fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
 
             $fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']);
@@ -178,7 +179,7 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide
                 'selectable' => true,
                 'leaf' => true,
                 'checked' => in_array($item[1], $selectedNodes),
-                'icon' => $item[3]
+                'icon' => $item[2]
             ];
         }
 
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTree.js
index 4c59809fa80e..a20c7221faf1 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTree.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTree.js
@@ -153,6 +153,11 @@ define(['d3', 'TYPO3/CMS/Backend/FormEngine/Element/SvgTree'], function (d3, Svg
             node.indeterminate = false;
         });
         this.calculateIndeterminate(this.rootNode);
+        // Initialise "value" attribute of input field after load and revalidate form engine fields
+        this.saveCheckboxes(this.rootNode);
+        if (typeof TYPO3.FormEngine.Validation !== 'undefined' && typeof TYPO3.FormEngine.Validation.validate === 'function') {
+            TYPO3.FormEngine.Validation.validate();
+        }
     };
 
     /**
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js
index f4bd5ac47ab9..93aab43337cd 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SvgTree.js
@@ -194,10 +194,46 @@ define(['jquery', 'd3'], function ($, d3) {
                     return;
                 }
                 if (Array.isArray(json)) {
-                    //little hack, so we can use json structure prepared by ExtJsJsonTreeRenderer
-                    json = json[0];
+                    if (json.length > 1) {
+                        // If tree comes with multiple root nodes, add them to a new root
+                        var tmp = {
+                            checked: undefined,
+                            children: [],
+                            expandable: true,
+                            expanded: true,
+                            iconTag: null,
+                            id: '',
+                            identifier: 'root',
+                            leaf: false,
+                            name: '',
+                            overlayIcon: '',
+                            text: '',
+                            uid: ''
+                        };
+                        for (var i = 0; i < json.length; i++) {
+                            var n = json[i];
+                            if (typeof n.identifier === 'undefined') {
+                                n.identifier = n.uid;
+                            }
+                            if (typeof n.name === 'undefined') {
+                                n.name = n.text;
+                            }
+                            if (typeof n.expandable === 'undefined') {
+                                n.expandable = true;
+                            }
+                            if (typeof n.expanded === 'undefined') {
+                                n.expanded = true;
+                            }
+                            if (typeof n.icon !== 'undefined') {
+                                n.iconTag = n.icon;
+                            }
+                            tmp.children.push(n);
+                        }
+                        json = tmp;
+                    } else {
+                        json = json[0];
+                    }
                 }
-
                 var rootNode = d3.hierarchy(json);
                 d3.tree(rootNode);
 
diff --git a/typo3/sysext/core/Classes/Category/CategoryRegistry.php b/typo3/sysext/core/Classes/Category/CategoryRegistry.php
index 6179b23260f9..fd70e9b1a08b 100644
--- a/typo3/sysext/core/Classes/Category/CategoryRegistry.php
+++ b/typo3/sysext/core/Classes/Category/CategoryRegistry.php
@@ -408,7 +408,7 @@ class CategoryRegistry implements SingletonInterface
                 'tablenames' => $tableName,
                 'fieldname' => $fieldName,
             ],
-            'size' => 50,
+            'size' => 20,
             'maxitems' => 9999,
             'treeConfig' => [
                 'parentField' => 'parent',
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-78628-TcaTreeTreePageTsConfigAddItemsIconPath.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-78628-TcaTreeTreePageTsConfigAddItemsIconPath.rst
new file mode 100644
index 000000000000..39f8d2e81af8
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-78628-TcaTreeTreePageTsConfigAddItemsIconPath.rst
@@ -0,0 +1,50 @@
+.. include:: ../../Includes.txt
+
+==============================================================
+Deprecation: #78628 - TCA tree pageTsConfig addItems icon path
+==============================================================
+
+See :issue:`78628`
+
+Description
+===========
+
+When adding items to `TCA` `type="select"` fields with `pageTSConfig`, the syntax for icons has been changed:
+
+Example to add an item with icon in pages to field category before:
+
+.. code-block:: typoscript
+
+    # Add an item with text "staticFromPageTs" to field category in pages
+    TCEFORM.pages.category.addItems.12345 = staticFromPageTs
+    # Assign icon to the element
+    TCEFORM.pages.category.addItems.12345.icon = EXT:any_extension/Resources/Public/Icons/Smiley.png
+
+The path has been deprecated and now accepts icon identifiers from the icon registry only:
+
+.. code-block:: typoscript
+
+    # Add an item with text "staticFromPageTs" to field category in pages
+    TCEFORM.pages.category.addItems.12345 = staticFromPageTs
+    # Assign icon to the element
+    TCEFORM.pages.category.addItems.12345.icon = my-registered-icon
+
+
+Impact
+======
+
+Using a file path syntax will trigger a deprecation log entry, but will work until TYPO3 v9.0.
+
+
+Affected Installations
+======================
+
+Instances that use this PageTSConfig setting with a file path instead of an icon identifier.
+
+
+Migration
+=========
+
+Register the icon within the :php:`IconRegistry` and use an icon identifier instead of the file path
+
+.. index:: TSConfig, Backend, TCA
diff --git a/typo3/sysext/frontend/Configuration/TCA/tt_content.php b/typo3/sysext/frontend/Configuration/TCA/tt_content.php
index 2572584b20d5..424f65cadfed 100644
--- a/typo3/sysext/frontend/Configuration/TCA/tt_content.php
+++ b/typo3/sysext/frontend/Configuration/TCA/tt_content.php
@@ -1015,7 +1015,7 @@ return [
                 'renderType' => 'selectTree',
                 'foreign_table' => 'sys_category',
                 'foreign_table_where' => 'AND sys_category.sys_language_uid IN (0,-1) ORDER BY sys_category.title ASC',
-                'size' => 50,
+                'size' => 20,
                 'maxitems' => 9999,
                 'treeConfig' => [
                     'parentField' => 'parent',
-- 
GitLab