Revert "[TASK] Avoid slow array functions in loops"
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / TcaTypesShowitem.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\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\MathUtility;
20
21 /**
22 * Create final showitem configuration in processedTca for types and palette
23 * fields
24 * Handles all the nasty details like subtypes_addlist and friends.
25 */
26 class TcaTypesShowitem implements FormDataProviderInterface
27 {
28 /**
29 * Processed TCA array
30 *
31 * @var array
32 */
33 protected $processedTca;
34
35 /**
36 * Set processedTca showitem
37 *
38 * @param array $result
39 * @return array
40 */
41 public function addData(array $result)
42 {
43 $this->processedTca = $result['processedTca'];
44
45 $recordTypeValue = $result['recordTypeValue'];
46
47 // Handle subtype_value_field, subtypes_addlist, subtypes_excludelist
48 if (!empty($result['processedTca']['types'][$recordTypeValue]['subtype_value_field'])) {
49 $subtypeFieldName = $result['processedTca']['types'][$recordTypeValue]['subtype_value_field'];
50 if (array_key_exists($subtypeFieldName, $result['databaseRow'])) {
51 $subtypeValue = $result['databaseRow'][$subtypeFieldName];
52 $result = $this->addFieldsBySubtypeAddList($result, $subtypeFieldName, $subtypeValue, $recordTypeValue);
53 $result = $this->removeFieldsBySubtypeExcludeList($result, $subtypeValue, $recordTypeValue);
54 }
55 }
56
57 // Handle bitmask_value_field, bitmask_excludelist_bits
58 if (!empty($result['processedTca']['types'][$recordTypeValue]['bitmask_value_field'])
59 && isset($result['processedTca']['types'][$recordTypeValue]['bitmask_excludelist_bits'])
60 && is_array($result['processedTca']['types'][$recordTypeValue]['bitmask_excludelist_bits'])
61 ) {
62 $bitmaskFieldName = $result['processedTca']['types'][$recordTypeValue]['bitmask_value_field'];
63 if (array_key_exists($bitmaskFieldName, $result['databaseRow'])) {
64 $bitmaskValue = $result['databaseRow'][$bitmaskFieldName];
65 $result = $this->removeFieldsByBitmaskExcludeBits($result, $bitmaskValue, $recordTypeValue);
66 }
67 }
68
69 // Handling of these parameters is finished. Unset them to not allow other handlers to fiddle with it.
70 // unset does not throw notice, even if not set
71 unset($result['processedTca']['types'][$recordTypeValue]['subtype_value_field']);
72 unset($result['processedTca']['types'][$recordTypeValue]['subtypes_excludelist']);
73 unset($result['processedTca']['types'][$recordTypeValue]['subtypes_addlist']);
74 unset($result['processedTca']['types'][$recordTypeValue]['bitmask_value_field']);
75 unset($result['processedTca']['types'][$recordTypeValue]['bitmask_excludelist_bits']);
76
77 return $result;
78 }
79
80 /**
81 * Insert additional fields in showitem based on subtypes_addlist
82 *
83 * databaseRow['theSubtypeValueField'] = 'theSubtypeValue'
84 * showitem = 'foo,theSubtypeValueField,bar'
85 * subtype_value_field = 'theSubtypeValueField'
86 * subtypes_addlist['theSubtypeValue'] = 'additionalField'
87 *
88 * -> showitem = 'foo,theSubtypeValueField,additionalField,bar'
89 *
90 * @param array $result Result array
91 * @param string $subtypeFieldName Field name holding subtype value
92 * @param string $subtypeValue subtype value
93 * @param string $recordTypeValue Given record type value
94 * @return array Modified result array
95 */
96 protected function addFieldsBySubtypeAddList(array $result, $subtypeFieldName, $subtypeValue, $recordTypeValue)
97 {
98 if (!empty($this->processedTca['types'][$recordTypeValue]['subtypes_addlist'][$subtypeValue])
99 && is_string($this->processedTca['types'][$recordTypeValue]['subtypes_addlist'][$subtypeValue])
100 ) {
101 $addListString = $this->processedTca['types'][$recordTypeValue]['subtypes_addlist'][$subtypeValue];
102 $addListArray = GeneralUtility::trimExplode(',', $addListString, true);
103 $showItemFieldString = $result['processedTca']['types'][$recordTypeValue]['showitem'];
104 $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, true);
105 // The "new" fields should be added after the subtype field itself, so find it
106 foreach ($showItemFieldArray as $index => $fieldConfigurationString) {
107 $found = false;
108 $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
109 $fieldName = $fieldConfigurationArray[0];
110 if ($fieldName === $subtypeFieldName) {
111 // Found the subtype value field in showitem
112 $found = true;
113 } elseif ($fieldName === '--palette--') {
114 // Try to find subtype value field in palette
115 if (isset($fieldConfigurationArray[2])) {
116 $paletteName = $fieldConfigurationArray[2];
117 if (!empty($this->processedTca['palettes'][$paletteName]['showitem'])) {
118 $paletteFields = GeneralUtility::trimExplode(',', $this->processedTca['palettes'][$paletteName]['showitem'], true);
119 foreach ($paletteFields as $paletteFieldConfiguration) {
120 $paletteFieldConfigurationArray = GeneralUtility::trimExplode(';', $paletteFieldConfiguration);
121 if ($paletteFieldConfigurationArray[0] === $subtypeFieldName) {
122 // Found it in palette
123 $found = true;
124 break;
125 }
126 }
127 }
128 }
129 }
130 if ($found) {
131 // Add fields between subtype field and next element
132 array_splice($showItemFieldArray, $index + 1, 0, $addListArray);
133 break;
134 }
135 }
136 $result['processedTca']['types'][$recordTypeValue]['showitem'] = implode(',', $showItemFieldArray);
137 }
138 return $result;
139 }
140
141 /**
142 * Remove fields from showitem based on subtypes_excludelist
143 *
144 * databaseRow['theSubtypeValueField'] = 'theSubtypeValue'
145 * showitem = 'foo,toRemove,bar'
146 * subtype_value_field = 'theSubtypeValueField'
147 * subtypes_excludelist['theSubtypeValue'] = 'toRemove'
148 *
149 * -> showitem = 'foo,bar'
150 *
151 * @param array $result Result array
152 * @param string $subtypeValue subtype value
153 * @param string $recordTypeValue Given record type value
154 * @return array Modified result array
155 */
156 protected function removeFieldsBySubtypeExcludeList(array $result, $subtypeValue, $recordTypeValue)
157 {
158 if (!empty($this->processedTca['types'][$recordTypeValue]['subtypes_excludelist'][$subtypeValue])
159 && is_string($this->processedTca['types'][$recordTypeValue]['subtypes_excludelist'][$subtypeValue])
160 ) {
161 $removeListString = $this->processedTca['types'][$recordTypeValue]['subtypes_excludelist'][$subtypeValue];
162 $removeListArray = GeneralUtility::trimExplode(',', $removeListString, true);
163 $result = $this->removeFields($result, $removeListArray, $recordTypeValue);
164 $result = $this->removeFieldsFromPalettes($result, $removeListArray);
165 }
166 return $result;
167 }
168
169 /**
170 * Remove fields from showitem based on subtypes_excludelist
171 *
172 * databaseRow['theSubtypeValueField'] = 5 // 1 0 1
173 * showitem = 'foo,toRemoveBy4,bar'
174 * bitmask_value_field = 'theSubtypeValueField'
175 * bitmask_excludelist_bits[+2] = 'toRemoveBy4'
176 *
177 * -> showitem = 'foo,bar'
178 *
179 * @param array $result Result array
180 * @param string $bitmaskValue subtype value
181 * @param string $recordTypeValue Given record type value
182 * @return array Modified result array
183 */
184 protected function removeFieldsByBitmaskExcludeBits(array $result, $bitmaskValue, $recordTypeValue)
185 {
186 $removeListArray = [];
187 $bitmaskValue = MathUtility::forceIntegerInRange($bitmaskValue, 0);
188 $excludeListBitsArray = $this->processedTca['types'][$recordTypeValue]['bitmask_excludelist_bits'];
189 foreach ($excludeListBitsArray as $bitKey => $excludeList) {
190 $bitKey = (int)$bitKey;
191 $isNegative = (bool)($bitKey < 0);
192 $bit = abs($bitKey);
193 if (!$isNegative && ($bitmaskValue & pow(2, $bit))
194 || $isNegative && !($bitmaskValue & pow(2, $bit))
195 ) {
196 $removeListArray = array_merge($removeListArray, GeneralUtility::trimExplode(',', $excludeList, true));
197 }
198 }
199 $result = $this->removeFields($result, $removeListArray, $recordTypeValue);
200 return $this->removeFieldsFromPalettes($result, $removeListArray);
201 }
202
203 /**
204 * Remove fields from show item field list
205 *
206 * @param array $result Given show item list
207 * @param array $removeListArray Fields to remove
208 * @param string $recordTypeValue Given record type value
209 * @return array Modified result array
210 */
211 protected function removeFields(array $result, array $removeListArray, $recordTypeValue)
212 {
213 $newFieldList = [];
214 $showItemFieldString = $result['processedTca']['types'][$recordTypeValue]['showitem'];
215 $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, true);
216 foreach ($showItemFieldArray as $fieldConfigurationString) {
217 $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
218 $fieldName = $fieldConfigurationArray[0];
219 if (!in_array($fieldConfigurationArray[0], $removeListArray, true)
220 // It does not make sense to exclude --palette-- and --div--
221 || $fieldName === '--palette--' || $fieldName === '--div--'
222 ) {
223 $newFieldList[] = $fieldConfigurationString;
224 }
225 }
226 $result['processedTca']['types'][$recordTypeValue]['showitem'] = implode(',', $newFieldList);
227 return $result;
228 }
229
230 /**
231 * Remove a list of element from all palettes
232 *
233 * @param array $result Result array
234 * @param array $removeListArray Array of elements to remove
235 * @return array Modified result array
236 * @todo: unit tests!
237 */
238 protected function removeFieldsFromPalettes(array $result, $removeListArray)
239 {
240 if (isset($result['processedTca']['palettes']) && is_array($result['processedTca']['palettes'])) {
241 foreach ($result['processedTca']['palettes'] as $paletteName => $paletteArray) {
242 if (!isset($paletteArray['showitem']) || !is_string($paletteArray['showitem'])) {
243 throw new \UnexpectedValueException(
244 'showitem field of palette ' . $paletteName . ' in table ' . $result['tableName'] . ' not found or not a string',
245 1439925240
246 );
247 }
248 $showItemFieldString = $paletteArray['showitem'];
249 $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, true);
250 $newFieldList = [];
251 foreach ($showItemFieldArray as $fieldConfigurationString) {
252 $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
253 $fieldName = $fieldConfigurationArray[0];
254 if (!in_array($fieldConfigurationArray[0], $removeListArray, true)
255 || $fieldName === '--linebreak--'
256 ) {
257 $newFieldList[] = $fieldConfigurationString;
258 }
259 }
260 $result['processedTca']['palettes'][$paletteName]['showitem'] = implode(',', $newFieldList);
261 }
262 }
263 return $result;
264 }
265 }