[TASK] Deprecate inline localizationMode
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / TcaInlineConfiguration.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\Utility\ArrayUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20
21 /**
22 * Set or initialize configuration for inline fields in TCA
23 */
24 class TcaInlineConfiguration implements FormDataProviderInterface
25 {
26 /**
27 * Find all inline fields and force proper configuration
28 *
29 * @param array $result
30 * @return array
31 * @throws \UnexpectedValueException If inline configuration is broken
32 */
33 public function addData(array $result)
34 {
35 foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
36 if (empty($fieldConfig['config']['type']) || $fieldConfig['config']['type'] !== 'inline') {
37 continue;
38 }
39
40 // Throw if an inline field without foreign_table is set
41 if (!isset($fieldConfig['config']['foreign_table'])) {
42 throw new \UnexpectedValueException(
43 'Inline field ' . $fieldName . ' of table ' . $result['tableName'] . ' must have a foreign_table config',
44 1443793404
45 );
46 }
47
48 $result = $this->initializeMinMaxItems($result, $fieldName);
49 $result = $this->initializeLocalizationMode($result, $fieldName);
50 $result = $this->initializeAppearance($result, $fieldName);
51 $result = $this->addInlineSelectorAndUniqueConfiguration($result, $fieldName);
52 }
53 return $result;
54 }
55
56 /**
57 * Set and validate minitems and maxitems in config
58 *
59 * @param array $result Result array
60 * @param string $fieldName Current handle field name
61 * @return array Modified item array
62 * @return array
63 */
64 protected function initializeMinMaxItems(array $result, $fieldName)
65 {
66 $config = $result['processedTca']['columns'][$fieldName]['config'];
67
68 $minItems = 0;
69 if (isset($config['minitems'])) {
70 $minItems = MathUtility::forceIntegerInRange($config['minitems'], 0);
71 }
72 $result['processedTca']['columns'][$fieldName]['config']['minitems'] = $minItems;
73
74 $maxItems = 99999;
75 if (isset($config['maxitems'])) {
76 $maxItems = MathUtility::forceIntegerInRange($config['maxitems'], 1);
77 }
78 $result['processedTca']['columns'][$fieldName]['config']['maxitems'] = $maxItems;
79
80 return $result;
81 }
82
83 /**
84 * Set appearance configuration
85 *
86 * @param array $result Result array
87 * @param string $fieldName Current handle field name
88 * @return array Modified item array
89 * @return array
90 */
91 protected function initializeAppearance(array $result, $fieldName)
92 {
93 $config = $result['processedTca']['columns'][$fieldName]['config'];
94 if (!isset($config['appearance']) || !is_array($config['appearance'])) {
95 // Init appearance if not set
96 $config['appearance'] = [];
97 }
98 // Set the position/appearance of the "Create new record" link
99 if (isset($config['foreign_selector']) && $config['foreign_selector']
100 && (!isset($config['appearance']['useCombination']) || !$config['appearance']['useCombination'])
101 ) {
102 $config['appearance']['levelLinksPosition'] = 'none';
103 } elseif (!isset($config['appearance']['levelLinksPosition'])
104 || !in_array($config['appearance']['levelLinksPosition'], ['top', 'bottom', 'both', 'none'], true)
105 ) {
106 $config['appearance']['levelLinksPosition'] = 'top';
107 }
108 $config['appearance']['showPossibleLocalizationRecords']
109 = isset($config['appearance']['showPossibleLocalizationRecords']) && $config['appearance']['showPossibleLocalizationRecords'];
110 $config['appearance']['showRemovedLocalizationRecords']
111 = isset($config['appearance']['showRemovedLocalizationRecords']) && $config['appearance']['showRemovedLocalizationRecords'];
112 // Defines which controls should be shown in header of each record
113 $enabledControls = [
114 'info' => true,
115 'new' => true,
116 'dragdrop' => true,
117 'sort' => true,
118 'hide' => true,
119 'delete' => true,
120 'localize' => true
121 ];
122 if (isset($config['appearance']['enabledControls']) && is_array($config['appearance']['enabledControls'])) {
123 $config['appearance']['enabledControls'] = array_merge($enabledControls, $config['appearance']['enabledControls']);
124 } else {
125 $config['appearance']['enabledControls'] = $enabledControls;
126 }
127 $result['processedTca']['columns'][$fieldName]['config'] = $config;
128
129 return $result;
130 }
131
132 /**
133 * Set localization mode. This will end up with localizationMode to be set to either 'select', 'keep'
134 * or 'none' if the handled record is a localized record.
135 *
136 * @see TcaInline for a detailed explanation on the meaning of these modes.
137 *
138 * @param array $result Result array
139 * @param string $fieldName Current handle field name
140 * @return array Modified item array
141 * @throws \UnexpectedValueException If localizationMode configuration is broken
142 */
143 protected function initializeLocalizationMode(array $result, $fieldName)
144 {
145 if ($result['defaultLanguageRow'] === null) {
146 // Currently handled parent is a localized row if a former provider added the "default" row
147 // If handled record is not localized, set localizationMode to 'none' and return
148 // @deprecated: IRRE 'localizationMode' is deprecated and will be removed in TYPO3 CMS 9
149 $result['processedTca']['columns'][$fieldName]['config']['behaviour']['localizationMode'] = 'none';
150 return $result;
151 }
152
153 $childTableName = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
154 $parentConfig = $result['processedTca']['columns'][$fieldName]['config'];
155
156 $isChildTableLocalizable = false;
157 if (isset($GLOBALS['TCA'][$childTableName]['ctrl']) && is_array($GLOBALS['TCA'][$childTableName]['ctrl'])
158 && isset($GLOBALS['TCA'][$childTableName]['ctrl']['languageField'])
159 && $GLOBALS['TCA'][$childTableName]['ctrl']['languageField']
160 && isset($GLOBALS['TCA'][$childTableName]['ctrl']['transOrigPointerField'])
161 && $GLOBALS['TCA'][$childTableName]['ctrl']['transOrigPointerField']
162 ) {
163 $isChildTableLocalizable = true;
164 }
165
166 $mode = null;
167
168 if (isset($parentConfig['behaviour']['localizationMode'])) {
169 // Use explicit set mode, but validate before use
170 // Use mode if set, but throw if not set to either 'select' or 'keep'
171 if ($parentConfig['behaviour']['localizationMode'] !== 'keep' && $parentConfig['behaviour']['localizationMode'] !== 'select') {
172 throw new \UnexpectedValueException(
173 'localizationMode of table ' . $result['tableName'] . ' field ' . $fieldName . ' is not valid, set to either \'keep\' or \'select\'',
174 1443829370
175 );
176 }
177 // Throw if is set to select, but child can not be localized
178 if ($parentConfig['behaviour']['localizationMode'] === 'select' && !$isChildTableLocalizable) {
179 throw new \UnexpectedValueException(
180 'Wrong configuration: localizationMode of table ' . $result['tableName'] . ' field ' . $fieldName . ' is set to \'select\', but table is not localizable.',
181 1443944274
182 );
183 }
184 $mode = $parentConfig['behaviour']['localizationMode'];
185 } else {
186 // Not set explicitly -> use "none"
187 $mode = 'none';
188 if ($isChildTableLocalizable) {
189 // Except if child is localizable, then use "select"
190 $mode = 'select';
191 }
192 }
193
194 // @deprecated: IRRE 'localizationMode' is deprecated and will be removed in TYPO3 CMS 9
195 $result['processedTca']['columns'][$fieldName]['config']['behaviour']['localizationMode'] = $mode;
196 return $result;
197 }
198
199 /**
200 * If foreign_selector or foreign_unique is set, this points to a field configuration of the child
201 * table. The InlineControlContainer may render a drop down field or an element browser later from this.
202 *
203 * Fetch configuration from child table configuration, sanitize and merge with
204 * foreign_selector_fieldTcaOverride that allows overriding this field definition again.
205 *
206 * Final configuration is written to selectorOrUniqueConfiguration of inline config section.
207 *
208 * @param array $result Result array
209 * @param string $fieldName Current handle field name
210 * @return array Modified item array
211 * @throws \UnexpectedValueException If configuration is broken
212 */
213 protected function addInlineSelectorAndUniqueConfiguration(array $result, $fieldName)
214 {
215 $config = $result['processedTca']['columns'][$fieldName]['config'];
216
217 // Early return if neither foreign_unique nor foreign_selector are set
218 if (!isset($config['foreign_unique']) && !isset($config['foreign_selector'])) {
219 return $result;
220 }
221
222 // If both are set, they must point to the same field
223 if (isset($config['foreign_unique']) && isset($config['foreign_selector'])
224 && $config['foreign_unique'] !== $config['foreign_selector']
225 ) {
226 throw new \UnexpectedValueException(
227 'Table ' . $result['tableName'] . ' field ' . $fieldName . ': If both foreign_unique and'
228 . ' foreign_selector are set, they must point to the same field',
229 1444995464
230 );
231 }
232
233 if (isset($config['foreign_unique'])) {
234 $fieldNameInChildConfiguration = $config['foreign_unique'];
235 } else {
236 $fieldNameInChildConfiguration = $config['foreign_selector'];
237 }
238
239 // Throw if field name in globals does not exist or is not of type select or group
240 if (!isset($GLOBALS['TCA'][$config['foreign_table']]['columns'][$fieldNameInChildConfiguration]['config']['type'])
241 || ($GLOBALS['TCA'][$config['foreign_table']]['columns'][$fieldNameInChildConfiguration]['config']['type'] !== 'select'
242 && $GLOBALS['TCA'][$config['foreign_table']]['columns'][$fieldNameInChildConfiguration]['config']['type'] !== 'group')
243 ) {
244 throw new \UnexpectedValueException(
245 'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points in foreign_selector or foreign_unique'
246 . ' to field ' . $fieldNameInChildConfiguration . ' of table ' . $config['foreign_table'] . ', but this field'
247 . ' is either not defined or is not of type select or group',
248 1444996537
249 );
250 }
251
252 $selectorOrUniqueConfiguration = [
253 'config' => $GLOBALS['TCA'][$config['foreign_table']]['columns'][$fieldNameInChildConfiguration]['config'],
254 ];
255
256 // Throw if field is type group, but not internal_type db
257 if ($selectorOrUniqueConfiguration['config']['type'] === 'group'
258 && (!isset($selectorOrUniqueConfiguration['config']['internal_type']) || $selectorOrUniqueConfiguration['config']['internal_type'] !== 'db')) {
259 throw new \UnexpectedValueException(
260 'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points in foreign_selector or foreign_unique'
261 . ' to field ' . $fieldNameInChildConfiguration . ' of table ' . $config['foreign_table'] . '. This field'
262 . ' is of type group and must be of internal_type db, which is not the case',
263 1444999130
264 );
265 }
266
267 // Merge foreign_selector_fieldTcaOverride if given
268 if (isset($config['foreign_selector'])
269 && isset($config['foreign_selector_fieldTcaOverride']['config'])
270 && is_array($config['foreign_selector_fieldTcaOverride']['config'])
271 ) {
272 ArrayUtility::mergeRecursiveWithOverrule($selectorOrUniqueConfiguration['config'], $config['foreign_selector_fieldTcaOverride']['config']);
273 }
274
275 // Add field name to config for easy access later
276 $selectorOrUniqueConfiguration['fieldName'] = $fieldNameInChildConfiguration;
277
278 // Add remote table name for easy access later
279 if ($selectorOrUniqueConfiguration['config']['type'] === 'select') {
280 if (!isset($selectorOrUniqueConfiguration['config']['foreign_table'])) {
281 throw new \UnexpectedValueException(
282 'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points in foreign_selector or foreign_unique'
283 . ' to field ' . $fieldNameInChildConfiguration . ' of table ' . $config['foreign_table'] . '. This field'
284 . ' is of type select and must define foreign_table',
285 1445078627
286 );
287 }
288 $foreignTable = $selectorOrUniqueConfiguration['config']['foreign_table'];
289 } else {
290 if (!isset($selectorOrUniqueConfiguration['config']['allowed'])) {
291 throw new \UnexpectedValueException(
292 'Table ' . $result['tableName'] . ' field ' . $fieldName . ' points in foreign_selector or foreign_unique'
293 . ' to field ' . $fieldNameInChildConfiguration . ' of table ' . $config['foreign_table'] . '. This field'
294 . ' is of type select and must define allowed',
295 1445078628
296 );
297 }
298 $foreignTable = $selectorOrUniqueConfiguration['config']['allowed'];
299 }
300 $selectorOrUniqueConfiguration['foreignTable'] = $foreignTable;
301
302 // If this is a foreign_selector field, mark it as such for data fetching later
303 $selectorOrUniqueConfiguration['isSelector'] = false;
304 if (isset($config['foreign_selector'])) {
305 $selectorOrUniqueConfiguration['isSelector'] = true;
306 }
307
308 // If this is a foreign_unique field, mark it a such for unique data fetching later
309 $selectorOrUniqueConfiguration['isUnique'] = false;
310 if (isset($config['foreign_unique'])) {
311 $selectorOrUniqueConfiguration['isUnique'] = true;
312 }
313
314 // Add field configuration to inline configuration
315 $result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'] = $selectorOrUniqueConfiguration;
316
317 return $result;
318 }
319 }