0756da2e0947a9cbc7de4786c946403fe638cc76
[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[] = GeneralUtility::trimExplode(',', $excludeList, true);
197 }
198 }
199 $removeListArray = array_merge(...$removeListArray);
200 $result = $this->removeFields($result, $removeListArray, $recordTypeValue);
201 return $this->removeFieldsFromPalettes($result, $removeListArray);
202 }
203
204 /**
205 * Remove fields from show item field list
206 *
207 * @param array $result Given show item list
208 * @param array $removeListArray Fields to remove
209 * @param string $recordTypeValue Given record type value
210 * @return array Modified result array
211 */
212 protected function removeFields(array $result, array $removeListArray, $recordTypeValue)
213 {
214 $newFieldList = [];
215 $showItemFieldString = $result['processedTca']['types'][$recordTypeValue]['showitem'];
216 $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, true);
217 foreach ($showItemFieldArray as $fieldConfigurationString) {
218 $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
219 $fieldName = $fieldConfigurationArray[0];
220 if (!in_array($fieldConfigurationArray[0], $removeListArray, true)
221 // It does not make sense to exclude --palette-- and --div--
222 || $fieldName === '--palette--' || $fieldName === '--div--'
223 ) {
224 $newFieldList[] = $fieldConfigurationString;
225 }
226 }
227 $result['processedTca']['types'][$recordTypeValue]['showitem'] = implode(',', $newFieldList);
228 return $result;
229 }
230
231 /**
232 * Remove a list of element from all palettes
233 *
234 * @param array $result Result array
235 * @param array $removeListArray Array of elements to remove
236 * @return array Modified result array
237 * @todo: unit tests!
238 */
239 protected function removeFieldsFromPalettes(array $result, $removeListArray)
240 {
241 if (isset($result['processedTca']['palettes']) && is_array($result['processedTca']['palettes'])) {
242 foreach ($result['processedTca']['palettes'] as $paletteName => $paletteArray) {
243 if (!isset($paletteArray['showitem']) || !is_string($paletteArray['showitem'])) {
244 throw new \UnexpectedValueException(
245 'showitem field of palette ' . $paletteName . ' in table ' . $result['tableName'] . ' not found or not a string',
246 1439925240
247 );
248 }
249 $showItemFieldString = $paletteArray['showitem'];
250 $showItemFieldArray = GeneralUtility::trimExplode(',', $showItemFieldString, true);
251 $newFieldList = [];
252 foreach ($showItemFieldArray as $fieldConfigurationString) {
253 $fieldConfigurationArray = GeneralUtility::trimExplode(';', $fieldConfigurationString);
254 $fieldName = $fieldConfigurationArray[0];
255 if (!in_array($fieldConfigurationArray[0], $removeListArray, true)
256 || $fieldName === '--linebreak--'
257 ) {
258 $newFieldList[] = $fieldConfigurationString;
259 }
260 }
261 $result['processedTca']['palettes'][$paletteName]['showitem'] = implode(',', $newFieldList);
262 }
263 }
264 return $result;
265 }
266 }