/**
  * @test
  */
 public function configuredDataProviderClassIsInstantiatedWithTcaConfigurationInConstructor()
 {
     $dataProviderMockClassName = TreeDataProviderWithConfigurationFixture::class;
     $tcaConfiguration = ['treeConfig' => ['dataProvider' => $dataProviderMockClassName], 'internal_type' => 'foo'];
     $this->setExpectedException(\RuntimeException::class, $this->anything(), 1438875249);
     $this->subject->getDataProvider($tcaConfiguration, 'foo', 'bar', array('uid' => 1));
 }
    /**
     * @test
     */
    public function configuredDataProviderClassIsInstantiatedWithTcaConfigurationInConstructor()
    {
        $dataProviderMockClassName = $this->getUniqueId('tx_coretest_tree_data_provider');
        $tcaConfiguration = array('treeConfig' => array('dataProvider' => $dataProviderMockClassName), 'internal_type' => 'foo');
        $classCode = 'class ' . $dataProviderMockClassName . ' {
			function __construct($configuration) {
				if (!is_array($configuration)) throw new Exception(\'Failed asserting that the constructor arguments are an array\');
				if ($configuration !== ' . var_export($tcaConfiguration, TRUE) . ') throw new Exception(\'Failed asserting that the constructor arguments are correctly passed\');
			}
		}';
        eval($classCode);
        $dataProvider = $this->subject->getDataProvider($tcaConfiguration, 'foo', 'bar', array('uid' => 1));
        $this->assertInstanceOf($dataProviderMockClassName, $dataProvider);
    }
 /**
  * Renders the Ext JS tree.
  *
  * @param array $result The current result array.
  * @param array $fieldConfig The configuration of the current field.
  * @param string $fieldName The name of the current field.
  * @param array $staticItems The static items from the field config.
  * @return array The tree data configuration
  */
 protected function renderTree(array $result, array $fieldConfig, $fieldName, array $staticItems)
 {
     $allowedUids = [];
     foreach ($fieldConfig['config']['items'] as $item) {
         if ((int) $item[1] > 0) {
             $allowedUids[] = $item[1];
         }
     }
     $treeDataProvider = TreeDataProviderFactory::getDataProvider($fieldConfig['config'], $result['tableName'], $fieldName, $result['databaseRow']);
     $treeDataProvider->setSelectedList(is_array($result['databaseRow'][$fieldName]) ? implode(',', $result['databaseRow'][$fieldName]) : $result['databaseRow'][$fieldName]);
     $treeDataProvider->setItemWhiteList($allowedUids);
     $treeDataProvider->initializeTreeData();
     /** @var ExtJsArrayTreeRenderer $treeRenderer */
     $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
     /** @var TableConfigurationTree $tree */
     $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
     $tree->setDataProvider($treeDataProvider);
     $tree->setNodeRenderer($treeRenderer);
     $treeItems = $this->prepareAdditionalItems($staticItems, $result['databaseRow'][$fieldName]);
     $treeItems[] = $tree->render();
     $treeConfig = ['items' => $treeItems, 'selectedNodes' => $this->prepareSelectedNodes($fieldConfig['config']['items'], $result['databaseRow'][$fieldName])];
     return $treeConfig;
 }
Example #4
0
    /**
     * Render tree widget
     *
     * @return array As defined in initializeResultArray() of AbstractNode
     */
    public function render()
    {
        $table = $this->data['tableName'];
        $field = $this->data['fieldName'];
        $row = $this->data['databaseRow'];
        $parameterArray = $this->data['parameterArray'];
        // Field configuration from TCA:
        $config = $parameterArray['fieldConf']['config'];
        $possibleSelectboxItems = $config['items'];
        $selectedNodes = $parameterArray['itemFormElValue'];
        $selectedNodesForApi = array();
        foreach ($selectedNodes as $selectedNode) {
            // @todo: this is ugly - the "old" pipe based value|label syntax is re-created here at the moment
            foreach ($possibleSelectboxItems as $possibleSelectboxItem) {
                if ((string) $possibleSelectboxItem[1] === (string) $selectedNode) {
                    $selectedNodesForApi[] = $selectedNode . '|' . rawurlencode($possibleSelectboxItem[0]);
                }
            }
        }
        $allowedUids = array();
        foreach ($possibleSelectboxItems as $item) {
            if ((int) $item[1] > 0) {
                $allowedUids[] = $item[1];
            }
        }
        $treeDataProvider = TreeDataProviderFactory::getDataProvider($config, $table, $field, $row);
        $treeDataProvider->setSelectedList(implode(',', $selectedNodes));
        $treeDataProvider->setItemWhiteList($allowedUids);
        $treeDataProvider->initializeTreeData();
        $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
        $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
        $tree->setDataProvider($treeDataProvider);
        $tree->setNodeRenderer($treeRenderer);
        $treeData = $tree->render();
        $itemArray = array();
        /**
         * @todo: Small bug here: In the past, this was the "not processed list" of default items, but now it is
         * @todo: a full list of elements. This needs to be fixed later, so "additional" default items are shown again.
        if (is_array($config['items'])) {
            foreach ($config['items'] as $additionalItem) {
                if ($additionalItem[1] !== '--div--') {
                    $item = new \stdClass();
                    $item->uid = $additionalItem[1];
                    $item->text = $this->getLanguageService()->sL($additionalItem[0]);
                    $item->selectable = TRUE;
                    $item->leaf = TRUE;
                    $item->checked = in_array($additionalItem[1], $selectedNodes);
                    if (file_exists(PATH_typo3 . $additionalItem[3])) {
                        $item->icon = $additionalItem[3];
                    } elseif (trim($additionalItem[3]) !== '') {
                        $item->iconCls = IconUtility::getSpriteIconClasses($additionalItem[3]);
                    }
                    $itemArray[] = $item;
                }
            }
        }
        */
        $itemArray[] = $treeData;
        $id = md5($parameterArray['itemFormElName']);
        if (isset($config['size']) && (int) $config['size'] > 0) {
            $height = (int) $config['size'] * 20;
        } else {
            $height = 280;
        }
        $autoSizeMax = null;
        if (isset($config['autoSizeMax']) && (int) $config['autoSizeMax'] > 0) {
            $autoSizeMax = (int) $config['autoSizeMax'] * 20;
        }
        $header = false;
        $expanded = false;
        $width = 280;
        $appearance = $config['treeConfig']['appearance'];
        if (is_array($appearance)) {
            $header = (bool) $appearance['showHeader'];
            $expanded = (bool) $appearance['expandAll'];
            if (isset($appearance['width'])) {
                $width = (int) $appearance['width'];
            }
        }
        $onChange = '';
        if ($parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) {
            $onChange = $parameterArray['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
        }
        // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
        // This is used for eg. "type" fields and others configured with "requestUpdate"
        if (!empty($GLOBALS['TCA'][$table]['ctrl']['type']) && $field === $GLOBALS['TCA'][$table]['ctrl']['type'] || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate']) && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)) {
            if ($this->getBackendUserAuthentication()->jsConfirmation(JsConfirmation::TYPE_CHANGE)) {
                $onChange = 'top.TYPO3.Modal.confirm(TBE_EDITOR.labels.refreshRequired.title, TBE_EDITOR.labels.refreshRequired.content).on("button.clicked", function(e) { if (e.target.name == "ok" && TBE_EDITOR.checkSubmit(-1)) { TBE_EDITOR.submitForm() } top.TYPO3.Modal.dismiss(); });';
            } else {
                $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
            }
        }
        $html = '
			<div class="typo3-tceforms-tree">
				<input class="treeRecord" type="hidden" ' . $this->getValidationDataAsDataAttribute($config) . ' data-formengine-input-name="' . htmlspecialchars($parameterArray['itemFormElName']) . '"' . ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"' . ' name="' . htmlspecialchars($parameterArray['itemFormElName']) . '" id="treeinput' . $id . '" value="' . htmlspecialchars(implode(',', $selectedNodesForApi)) . '" />
			</div>
			<div id="tree_' . $id . '">

			</div>';
        // Wizards:
        if (empty($config['readOnly'])) {
            $html = $this->renderWizards(array($html), $config['wizards'], $table, $row, $field, $parameterArray, $parameterArray['itemFormElName'], BackendUtility::getSpecConfParts($parameterArray['fieldConf']['defaultExtras']));
        }
        $resultArray = $this->initializeResultArray();
        $resultArray['extJSCODE'] .= LF . 'Ext.onReady(function() {
			TYPO3.Components.Tree.StandardTreeItemData["' . $id . '"] = ' . json_encode($itemArray) . ';
			var tree' . $id . ' = new TYPO3.Components.Tree.StandardTree({
				id: "' . $id . '",
				showHeader: ' . (int) $header . ',
				onChange: ' . GeneralUtility::quoteJSvalue($onChange) . ',
				countSelectedNodes: ' . count($selectedNodesForApi) . ',
				width: ' . (int) $width . ',
				listeners: {
					click: function(node, event) {
						if (typeof(node.attributes.checked) == "boolean") {
							node.attributes.checked = ! node.attributes.checked;
							node.getUI().toggleCheck(node.attributes.checked);
						}
					},
					dblclick: function(node, event) {
						if (typeof(node.attributes.checked) == "boolean") {
							node.attributes.checked = ! node.attributes.checked;
							node.getUI().toggleCheck(node.attributes.checked);
						}
					},
					checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,
					collapsenode: function(node) {
						if (node.id !== "root") {
							top.TYPO3.Storage.Persistent.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);
						}
					},
					expandnode: function(node) {
						if (node.id !== "root") {
							top.TYPO3.Storage.Persistent.addToList("tcaTrees." + this.ucId, node.attributes.uid);
						}
					},
					beforerender: function(treeCmp) {
						// Check if that tree element is already rendered. It is appended on the first tceforms_inline call.
						if (Ext.fly(treeCmp.getId())) {
							return false;
						}
					}' . ($expanded ? ',
					afterrender: function(treeCmp) {
						treeCmp.expandAll();
					}' : '') . '
				},
				tcaMaxItems: ' . ($config['maxitems'] ? (int) $config['maxitems'] : 99999) . ',
				tcaSelectRecursiveAllowed: ' . ($appearance['allowRecursiveMode'] ? 'true' : 'false') . ',
				tcaSelectRecursive: false,
				tcaExclusiveKeys: "' . ($config['exclusiveKeys'] ? $config['exclusiveKeys'] : '') . '",
				ucId: "' . md5($table . '|' . $field) . '",
				selModel: TYPO3.Components.Tree.EmptySelectionModel,
				disabled: ' . ($config['readOnly'] ? 'true' : 'false') . '
			});' . LF . ($autoSizeMax ? 'tree' . $id . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";' : 'tree' . $id . '.height = ' . $height . ';') . LF . 'window.setTimeout(function() {
				tree' . $id . '.render("tree_' . $id . '");
			}, 200);
		});';
        $resultArray['html'] = $html;
        return $resultArray;
    }
Example #5
0
    /**
     * renders the tree as replacement for the selector
     *
     * @param string $table The table name of the record
     * @param string $field The field name which this element is supposed to edit
     * @param array $row The record data array where the value(s) for the field can be found
     * @param array $PA An array with additional configuration options.
     * @param array $config (Redundant) content of $PA['fieldConf']['config'] (for convenience)
     * @param array $possibleSelectboxItems Items available for selection
     * @param string $noMatchLabel Label for no-matching-value
     * @return string The HTML code for the TCEform field
     */
    public function renderField($table, $field, $row, &$PA, $config, $possibleSelectboxItems, $noMatchLabel)
    {
        $valueArray = array();
        $selectedNodes = array();
        if (!empty($PA['itemFormElValue'])) {
            $valueArray = explode(',', $PA['itemFormElValue']);
        }
        if (count($valueArray)) {
            foreach ($valueArray as $selectedValue) {
                $temp = explode('|', $selectedValue);
                $selectedNodes[] = $temp[0];
            }
        }
        $allowedUids = array();
        foreach ($possibleSelectboxItems as $item) {
            if ((int) $item[1] > 0) {
                $allowedUids[] = $item[1];
            }
        }
        $treeDataProvider = \TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory::getDataProvider($config, $table, $field, $row);
        $treeDataProvider->setSelectedList(implode(',', $selectedNodes));
        $treeDataProvider->setItemWhiteList($allowedUids);
        $treeDataProvider->initializeTreeData();
        $treeRenderer = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\ExtJsArrayTreeRenderer');
        $tree = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfigurationTree');
        $tree->setDataProvider($treeDataProvider);
        $tree->setNodeRenderer($treeRenderer);
        $treeData = $tree->render();
        $itemArray = array();
        if (is_array($PA['fieldConf']['config']['items'])) {
            foreach ($PA['fieldConf']['config']['items'] as $additionalItem) {
                if ($additionalItem[1] !== '--div--') {
                    $item = new \stdClass();
                    $item->uid = $additionalItem[1];
                    $item->text = $GLOBALS['LANG']->sL($additionalItem[0]);
                    $item->selectable = TRUE;
                    $item->leaf = TRUE;
                    $item->checked = in_array($additionalItem[1], $selectedNodes);
                    if (file_exists(PATH_typo3 . $additionalItem[3])) {
                        $item->icon = $additionalItem[3];
                    } elseif (strlen(trim($additionalItem[3]))) {
                        $item->iconCls = \TYPO3\CMS\Backend\Utility\IconUtility::getSpriteIconClasses($additionalItem[3]);
                    }
                    $itemArray[] = $item;
                }
            }
        }
        $itemArray[] = $treeData;
        $treeData = json_encode($itemArray);
        $id = md5($PA['itemFormElName']);
        if (isset($PA['fieldConf']['config']['size']) && (int) $PA['fieldConf']['config']['size'] > 0) {
            $height = (int) $PA['fieldConf']['config']['size'] * 20;
        } else {
            $height = 280;
        }
        if (isset($PA['fieldConf']['config']['autoSizeMax']) && (int) $PA['fieldConf']['config']['autoSizeMax'] > 0) {
            $autoSizeMax = (int) $PA['fieldConf']['config']['autoSizeMax'] * 20;
        }
        $header = FALSE;
        $expanded = FALSE;
        $width = 280;
        $appearance = $PA['fieldConf']['config']['treeConfig']['appearance'];
        if (is_array($appearance)) {
            $header = $appearance['showHeader'] ? TRUE : FALSE;
            $expanded = $appearance['expandAll'] === TRUE;
            if (isset($appearance['width'])) {
                $width = (int) $appearance['width'];
            }
        }
        $onChange = '';
        if ($PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged']) {
            $onChange = $PA['fieldChangeFunc']['TBE_EDITOR_fieldChanged'];
        }
        // Create a JavaScript code line which will ask the user to save/update the form due to changing the element.
        // This is used for eg. "type" fields and others configured with "requestUpdate"
        if (!empty($GLOBALS['TCA'][$table]['ctrl']['type']) && $field === $GLOBALS['TCA'][$table]['ctrl']['type'] || !empty($GLOBALS['TCA'][$table]['ctrl']['requestUpdate']) && GeneralUtility::inList(str_replace(' ', '', $GLOBALS['TCA'][$table]['ctrl']['requestUpdate']), $field)) {
            if ($GLOBALS['BE_USER']->jsConfirmation(1)) {
                $onChange .= 'if (confirm(TBE_EDITOR.labels.onChangeAlert) && ' . 'TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
            } else {
                $onChange .= 'if (TBE_EDITOR.checkSubmit(-1)){ TBE_EDITOR.submitForm() };';
            }
        }
        /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
        $pageRenderer = $GLOBALS['SOBE']->doc->getPageRenderer();
        $pageRenderer->loadExtJs();
        $pageRenderer->addJsFile('sysext/backend/Resources/Public/JavaScript/tree.js');
        $pageRenderer->addInlineLanguageLabelFile(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('lang') . 'locallang_csh_corebe.xlf', 'tcatree');
        $pageRenderer->addExtOnReadyCode('
			TYPO3.Components.Tree.StandardTreeItemData["' . $id . '"] = ' . $treeData . ';
			var tree' . $id . ' = new TYPO3.Components.Tree.StandardTree({
				id: "' . $id . '",
				showHeader: ' . (int) $header . ',
				onChange: "' . $onChange . '",
				countSelectedNodes: ' . count($selectedNodes) . ',
				width: ' . $width . ',
				listeners: {
					click: function(node, event) {
						if (typeof(node.attributes.checked) == "boolean") {
							node.attributes.checked = ! node.attributes.checked;
							node.getUI().toggleCheck(node.attributes.checked);
						}
					},
					dblclick: function(node, event) {
						if (typeof(node.attributes.checked) == "boolean") {
							node.attributes.checked = ! node.attributes.checked;
							node.getUI().toggleCheck(node.attributes.checked);
						}
					},
					checkchange: TYPO3.Components.Tree.TcaCheckChangeHandler,
					collapsenode: function(node) {
						if (node.id !== "root") {
							top.TYPO3.BackendUserSettings.ExtDirect.removeFromList("tcaTrees." + this.ucId, node.attributes.uid);
						}
					},
					expandnode: function(node) {
						if (node.id !== "root") {
							top.TYPO3.BackendUserSettings.ExtDirect.addToList("tcaTrees." + this.ucId, node.attributes.uid);
						}
					},
					beforerender: function(treeCmp) {
						// Check if that tree element is already rendered. It is appended on the first tceforms_inline call.
						if (Ext.fly(treeCmp.getId())) {
							return false;
						}
					}' . ($expanded ? ',
					afterrender: function(treeCmp) {
						treeCmp.expandAll();
					}' : '') . '
				},
				tcaMaxItems: ' . ($PA['fieldConf']['config']['maxitems'] ? (int) $PA['fieldConf']['config']['maxitems'] : 99999) . ',
				tcaSelectRecursiveAllowed: ' . ($appearance['allowRecursiveMode'] ? 'true' : 'false') . ',
				tcaSelectRecursive: false,
				tcaExclusiveKeys: "' . ($PA['fieldConf']['config']['exclusiveKeys'] ? $PA['fieldConf']['config']['exclusiveKeys'] : '') . '",
				ucId: "' . md5($table . '|' . $field) . '",
				selModel: TYPO3.Components.Tree.EmptySelectionModel,
				disabled: ' . ($PA['fieldConf']['config']['readOnly'] ? 'true' : 'false') . '
			});' . LF . ($autoSizeMax ? 'tree' . $id . '.bodyStyle = "max-height: ' . $autoSizeMax . 'px;min-height: ' . $height . 'px;";' : 'tree' . $id . '.height = ' . $height . ';') . LF . '(function() {
					tree' . $id . '.render("tree_' . $id . '");
				}).defer(20);
		');
        $formField = '
			<div class="typo3-tceforms-tree">
				<input class="treeRecord" type="hidden" name="' . htmlspecialchars($PA['itemFormElName']) . '" id="treeinput' . $id . '" value="' . htmlspecialchars($PA['itemFormElValue']) . '" />
			</div>
			<div id="tree_' . $id . '">

			</div>';
        return $formField;
    }
Example #6
0
 /**
  * Sanitize config options and resolve select items if requested.
  *
  * @param array $result
  * @return array
  * @throws \UnexpectedValueException
  */
 public function addData(array $result)
 {
     $table = $result['tableName'];
     foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
         if (empty($fieldConfig['config']['type']) || $fieldConfig['config']['type'] !== 'select') {
             continue;
         }
         // Make sure we are only processing supported renderTypes
         if (!$this->isTargetRenderType($fieldConfig)) {
             continue;
         }
         $fieldConfig['config']['maxitems'] = $this->sanitizeMaxItems($fieldConfig['config']['maxitems']);
         // A couple of tree specific config parameters can be overwritten via page TS.
         // Pick those that influence the data fetching and write them into the config
         // given to the tree data provider. This is additionally used in SelectTreeElement, so always do that.
         if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['config.']['treeConfig.'])) {
             $pageTsConfig = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['config.']['treeConfig.'];
             // If rootUid is set in pageTsConfig, use it
             if (isset($pageTsConfig['rootUid'])) {
                 $fieldConfig['config']['treeConfig']['rootUid'] = (int) $pageTsConfig['rootUid'];
             }
             if (isset($pageTsConfig['appearance.']['expandAll'])) {
                 $fieldConfig['config']['treeConfig']['appearance']['expandAll'] = (bool) $pageTsConfig['appearance.']['expandAll'];
             }
             if (isset($pageTsConfig['appearance.']['maxLevels'])) {
                 $fieldConfig['config']['treeConfig']['appearance']['maxLevels'] = (int) $pageTsConfig['appearance.']['maxLevels'];
             }
             if (isset($pageTsConfig['appearance.']['nonSelectableLevels'])) {
                 $fieldConfig['config']['treeConfig']['appearance']['nonSelectableLevels'] = $pageTsConfig['appearance.']['nonSelectableLevels'];
             }
         }
         if ($result['selectTreeCompileItems']) {
             // Prepare the list of currently selected nodes using RelationHandler
             $result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName);
             $result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, []);
             $finalItems = [];
             // Prepare the list of "static" items if there are any.
             // "static" and "dynamic" is separated since the tree code only copes with "real" existing foreign nodes,
             // so this "static" stuff allows defining tree items that don't really exist in the tree.
             $itemsFromTca = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName);
             // List of additional items defined by page ts config "addItems"
             $itemsFromPageTsConfig = $this->addItemsFromPageTsConfig($result, $fieldName, []);
             if (!empty($itemsFromTca) || !empty($itemsFromPageTsConfig)) {
                 // First apply "keepItems" to $itemsFromTca, this will restrict the tca item list to only
                 // those items that are defined in page ts "keepItems" if given
                 $itemsFromTca = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $itemsFromTca);
                 // Then, merge the items from page ts "addItems" into item list, since "addItems" should
                 // add additional items even if they are not in the "keepItems" list
                 $staticItems = array_merge($itemsFromTca, $itemsFromPageTsConfig);
                 // Now apply page ts config "removeItems", so this is *after* addItems, so "removeItems" could
                 // possibly remove items again that were added via "addItems"
                 $staticItems = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $staticItems);
                 // Now, apply user and access right restrictions to this item list
                 $staticItems = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $staticItems);
                 $staticItems = $this->removeItemsByUserAuthMode($result, $fieldName, $staticItems);
                 $staticItems = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $staticItems);
                 // Call itemsProcFunc if given. Note this function does *not* see the "dynamic" list of items
                 if (!empty($fieldConfig['config']['itemsProcFunc'])) {
                     $staticItems = $this->resolveItemProcessorFunction($result, $fieldName, $staticItems);
                     // itemsProcFunc must not be used anymore
                     unset($fieldConfig['config']['itemsProcFunc']);
                 }
                 // And translate any labels from the static list
                 $staticItems = $this->translateLabels($result, $staticItems, $table, $fieldName);
                 // Now compile the target items using the same array structure as the "dynamic" list below
                 foreach ($staticItems as $item) {
                     if ($item[1] === '--div--') {
                         // Skip divs that may occur here for whatever reason
                         continue;
                     }
                     $finalItems[] = ['identifier' => $item[1], 'name' => $item[0], 'icon' => $item[2] ?? '', 'iconOverlay' => '', 'depth' => 0, 'hasChildren' => false, 'selectable' => true, 'checked' => in_array($item[1], $result['databaseRow'][$fieldName])];
                 }
             }
             // Fetch the list of all possible "related" items (yuk!) and apply a similar processing as with the "static" list
             $dynamicItems = $this->addItemsFromForeignTable($result, $fieldName, []);
             $dynamicItems = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $dynamicItems);
             $dynamicItems = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $dynamicItems);
             $dynamicItems = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $dynamicItems);
             $dynamicItems = $this->removeItemsByUserAuthMode($result, $fieldName, $dynamicItems);
             $dynamicItems = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $dynamicItems);
             // Funnily, the only data needed for the tree code are the uids of the possible records (yuk!) - get them
             $uidListOfAllDynamicItems = [];
             foreach ($dynamicItems as $item) {
                 if ((int) $item[1] > 0) {
                     $uidListOfAllDynamicItems[] = (int) $item[1];
                 }
             }
             // Now kick in this tree stuff
             $treeDataProvider = TreeDataProviderFactory::getDataProvider($fieldConfig['config'], $table, $fieldName, $result['databaseRow']);
             $treeDataProvider->setSelectedList(implode(',', $result['databaseRow'][$fieldName]));
             // Basically the tree foo fetches all tree nodes again (aaargs), then verifies if
             // a given rows uid is within this "list of allowed uids". It then creates an object
             // tree representing the nested tree, just to collapse all that to a flat array again. Yay ...
             $treeDataProvider->setItemWhiteList($uidListOfAllDynamicItems);
             $treeDataProvider->initializeTreeData();
             $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
             $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
             $tree->setDataProvider($treeDataProvider);
             $tree->setNodeRenderer($treeRenderer);
             // Merge tree nodes after calculated nodes from static items
             $fieldConfig['config']['items'] = array_merge($finalItems, $tree->render());
         }
         $result['processedTca']['columns'][$fieldName] = $fieldConfig;
     }
     return $result;
 }