[BUGFIX] selectTree pageTsConfig addItems
[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 $pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []);
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'] + $pageTsConfigAddItems;
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'] = $pageTsConfigAddItems + $fieldConfig['config']['items'];
62 $fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']);
63
64 $fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']);
65 $fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']);
66 $fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']);
67
68 // Resolve "itemsProcFunc"
69 if (!empty($fieldConfig['config']['itemsProcFunc'])) {
70 $fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']);
71 // itemsProcFunc must not be used anymore
72 unset($fieldConfig['config']['itemsProcFunc']);
73 }
74
75 // Translate labels
76 $fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName);
77
78 $staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems);
79 $result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName);
80 $result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues);
81
82 // Keys may contain table names, so a numeric array is created
83 $fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']);
84
85 // A couple of tree specific config parameters can be overwritten via page TS.
86 // Pick those that influence the data fetching and write them into the config
87 // given to the tree data provider
88 if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['config.']['treeConfig.'])) {
89 $pageTsConfig = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['config.']['treeConfig.'];
90 // If rootUid is set in pageTsConfig, use it
91 if (isset($pageTsConfig['rootUid'])) {
92 $fieldConfig['config']['treeConfig']['rootUid'] = (int)$pageTsConfig['rootUid'];
93 }
94 if (isset($pageTsConfig['appearance.']['expandAll'])) {
95 $fieldConfig['config']['treeConfig']['appearance']['expandAll'] = (bool)$pageTsConfig['appearance.']['expandAll'];
96 }
97 if (isset($pageTsConfig['appearance.']['maxLevels'])) {
98 $fieldConfig['config']['treeConfig']['appearance']['maxLevels'] = (int)$pageTsConfig['appearance.']['maxLevels'];
99 }
100 if (isset($pageTsConfig['appearance.']['nonSelectableLevels'])) {
101 $fieldConfig['config']['treeConfig']['appearance']['nonSelectableLevels'] = $pageTsConfig['appearance.']['nonSelectableLevels'];
102 }
103 }
104
105 $fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems);
106
107 $result['processedTca']['columns'][$fieldName] = $fieldConfig;
108 }
109
110 return $result;
111 }
112
113 /**
114 * Renders the Ext JS tree.
115 *
116 * @param array $result The current result array.
117 * @param array $fieldConfig The configuration of the current field.
118 * @param string $fieldName The name of the current field.
119 * @param array $staticItems The static items from the field config.
120 * @return array The tree data configuration
121 */
122 protected function renderTree(array $result, array $fieldConfig, $fieldName, array $staticItems)
123 {
124 $allowedUids = [];
125 foreach ($fieldConfig['config']['items'] as $item) {
126 if ((int)$item[1] > 0) {
127 $allowedUids[] = $item[1];
128 }
129 }
130
131 $treeDataProvider = TreeDataProviderFactory::getDataProvider(
132 $fieldConfig['config'],
133 $result['tableName'],
134 $fieldName,
135 $result['databaseRow']
136 );
137 $treeDataProvider->setSelectedList(is_array($result['databaseRow'][$fieldName]) ? implode(',', $result['databaseRow'][$fieldName]) : $result['databaseRow'][$fieldName]);
138 $treeDataProvider->setItemWhiteList($allowedUids);
139 $treeDataProvider->initializeTreeData();
140
141 /** @var ExtJsArrayTreeRenderer $treeRenderer */
142 $treeRenderer = GeneralUtility::makeInstance(ExtJsArrayTreeRenderer::class);
143
144 /** @var TableConfigurationTree $tree */
145 $tree = GeneralUtility::makeInstance(TableConfigurationTree::class);
146 $tree->setDataProvider($treeDataProvider);
147 $tree->setNodeRenderer($treeRenderer);
148
149 $treeItems = $this->prepareAdditionalItems($staticItems, $result['databaseRow'][$fieldName]);
150 $treeItems[] = $tree->render();
151
152 $treeConfig = [
153 'items' => $treeItems,
154 'selectedNodes' => $this->prepareSelectedNodes($fieldConfig['config']['items'], $result['databaseRow'][$fieldName])
155 ];
156
157 return $treeConfig;
158 }
159
160 /**
161 * Prepare the additional items that get prepended to the tree as leaves
162 *
163 * @param array $itemArray
164 * @param array $selectedNodes
165 * @return array
166 */
167 protected function prepareAdditionalItems(array $itemArray, array $selectedNodes)
168 {
169 $additionalItems = [];
170
171 foreach ($itemArray as $item) {
172 if ($item[1] === '--div--') {
173 continue;
174 }
175
176 $additionalItems[] = [
177 'uid' => $item[1],
178 'text' => $item[0],
179 'selectable' => true,
180 'leaf' => true,
181 'checked' => in_array($item[1], $selectedNodes),
182 'icon' => $item[2]
183 ];
184 }
185
186 return $additionalItems;
187 }
188
189 /**
190 * Make sure to only keep the selected nodes that are really available in the database and for the user
191 * (e.g. after permissions etc)
192 *
193 * @param array $itemArray
194 * @param array $databaseValues
195 * @return array
196 * @todo: this is ugly - should be removed with the tree rewrite
197 */
198 protected function prepareSelectedNodes(array $itemArray, array $databaseValues)
199 {
200 $selectedNodes = [];
201 if (!empty($databaseValues)) {
202 foreach ($databaseValues as $selectedNode) {
203 foreach ($itemArray as $possibleSelectBoxItem) {
204 if ((string)$possibleSelectBoxItem[1] === (string)$selectedNode) {
205 $selectedNodes[] = $selectedNode;
206 }
207 }
208 }
209 }
210
211 return $selectedNodes;
212 }
213
214 /**
215 * Determines whether the current field is a valid target for this DataProvider
216 *
217 * @param array $fieldConfig
218 * @return bool
219 */
220 protected function isTargetRenderType(array $fieldConfig)
221 {
222 return $fieldConfig['config']['renderType'] === 'selectTree';
223 }
224 }