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