[BUGFIX] FormEngine: Initialize validation of new flexform sections
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / TcaSelectTreeItems.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\FormDataProvider;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
18 use TYPO3\CMS\Core\Tree\TableConfiguration\ExtJsArrayTreeRenderer;
19 use TYPO3\CMS\Core\Tree\TableConfiguration\TableConfigurationTree;
20 use TYPO3\CMS\Core\Tree\TableConfiguration\TreeDataProviderFactory;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Resolve select items, set processed item list in processedTca, sanitize and resolve database field
25 */
26 class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProviderInterface
27 {
28 /**
29 * Resolve select items
30 *
31 * @param array $result
32 * @return array
33 * @throws \UnexpectedValueException
34 */
35 public function addData(array $result)
36 {
37 $table = $result['tableName'];
38
39 foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
40 if (empty($fieldConfig['config']['type']) || $fieldConfig['config']['type'] !== 'select') {
41 continue;
42 }
43
44 // Make sure we are only processing supported renderTypes
45 if (!$this->isTargetRenderType($fieldConfig)) {
46 continue;
47 }
48
49 $fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName);
50 $fieldConfig['config']['maxitems'] = $this->sanitizeMaxItems($fieldConfig['config']['maxitems']);
51
52 $fieldConfig['config']['items'] = $this->addItemsFromPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
53 $fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']);
54 $fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']);
55 $staticItems = $fieldConfig['config']['items'];
56
57 $fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']);
58 $dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems);
59
60 $fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
61 $fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
62 $fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']);
63 $fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']);
64 $fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']);
65
66 // Resolve "itemsProcFunc"
67 if (!empty($fieldConfig['config']['itemsProcFunc'])) {
68 $fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']);
69 // itemsProcFunc must not be used anymore
70 unset($fieldConfig['config']['itemsProcFunc']);
71 }
72
73 // Translate labels
74 $fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName);
75
76 $staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems);
77 $result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName);
78 $result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues);
79
80 // Keys may contain table names, so a numeric array is created
81 $fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']);
82
83 $fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems);
84
85 $result['processedTca']['columns'][$fieldName] = $fieldConfig;
86 }
87
88 return $result;
89 }
90
91 /**
92 * Renders the Ext JS tree.
93 *
94 * @param array $result The current result array.
95 * @param array $fieldConfig The configuration of the current field.
96 * @param string $fieldName The name of the current field.
97 * @param array $staticItems The static items from the field config.
98 * @return array The tree data configuration
99 */
100 protected function renderTree(array $result, array $fieldConfig, $fieldName, array $staticItems)
101 {
102 $allowedUids = [];
103 foreach ($fieldConfig['config']['items'] as $item) {
104 if ((int)$item[1] > 0) {
105 $allowedUids[] = $item[1];
106 }
107 }
108
109 $treeDataProvider = TreeDataProviderFactory::getDataProvider(
110 $fieldConfig['config'],
111 $result['tableName'],
112 $fieldName,
113 $result['databaseRow']
114 );
115 $treeDataProvider->setSelectedList(is_array($result['databaseRow'][$fieldName]) ? implode(',', $result['databaseRow'][$fieldName]) : $result['databaseRow'][$fieldName]);
116 $treeDataProvider->setItemWhiteList($allowedUids);
117 $treeDataProvider->initializeTreeData();
118
119 /** @var ExtJsArrayTreeRenderer $treeRenderer */
120 $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
121
122 /** @var TableConfigurationTree $tree */
123 $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
124 $tree->setDataProvider($treeDataProvider);
125 $tree->setNodeRenderer($treeRenderer);
126
127 $treeItems = $this->prepareAdditionalItems($staticItems, $result['databaseRow'][$fieldName]);
128 $treeItems[] = $tree->render();
129
130 $treeConfig = [
131 'items' => $treeItems,
132 'selectedNodes' => $this->prepareSelectedNodes($fieldConfig['config']['items'], $result['databaseRow'][$fieldName])
133 ];
134
135 return $treeConfig;
136 }
137
138 /**
139 * Prepare the additional items that get prepended to the tree as leaves
140 *
141 * @param array $itemArray
142 * @param array $selectedNodes
143 * @return array
144 */
145 protected function prepareAdditionalItems(array $itemArray, array $selectedNodes)
146 {
147 $additionalItems = [];
148
149 foreach ($itemArray as $item) {
150 if ($item[1] === '--div--') {
151 continue;
152 }
153
154 $additionalItems[] = [
155 'uid' => $item[1],
156 'text' => $item[0],
157 'selectable' => true,
158 'leaf' => true,
159 'checked' => in_array($item[1], $selectedNodes),
160 'icon' => $item[3]
161 ];
162 }
163
164 return $additionalItems;
165 }
166
167 /**
168 * Re-create the old pipe based syntax of selected nodes for the ExtJS rendering part
169 *
170 * @param array $itemArray
171 * @param array $databaseValues
172 * @return array
173 * @todo: this is ugly - should be removed with the tree rewrite
174 */
175 protected function prepareSelectedNodes(array $itemArray, array $databaseValues)
176 {
177 $selectedNodes = [];
178 if (!empty($databaseValues)) {
179 foreach ($databaseValues as $selectedNode) {
180 foreach ($itemArray as $possibleSelectBoxItem) {
181 if ((string)$possibleSelectBoxItem[1] === (string)$selectedNode) {
182 $selectedNodes[] = $selectedNode . '|' . rawurlencode($possibleSelectBoxItem[0]);
183 }
184 }
185 }
186 }
187
188 return $selectedNodes;
189 }
190
191 /**
192 * Determines whether the current field is a valid target for this DataProvider
193 *
194 * @param array $fieldConfig
195 * @return bool
196 */
197 protected function isTargetRenderType(array $fieldConfig)
198 {
199 return $fieldConfig['config']['renderType'] === 'selectTree';
200 }
201 }