[FEATURE] Override check/radio labels with TSConfig
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FlexFormsHelper.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form;
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\Core\Utility\GeneralUtility;
18
19 /**
20 * Contains FlexForm manipulation methods as part of the TCEforms
21 *
22 * @author Kai Vogel <kai.vogel(at)speedprogs.de>
23 */
24 class FlexFormsHelper extends \TYPO3\CMS\Backend\Form\FormEngine {
25
26 /**
27 * Options that will be removed from config after creating items for a select to prevent double parsing
28 *
29 * @var array
30 */
31 protected $removeSelectConfig = array(
32 'itemsProcFunc',
33 'foreign_table',
34 'foreign_table_where',
35 'foreign_table_prefix',
36 'foreign_table_loadIcons',
37 'neg_foreign_table',
38 'neg_foreign_table_where',
39 'neg_foreign_table_prefix',
40 'neg_foreign_table_loadIcons',
41 'neg_foreign_table_imposeValueField',
42 'fileFolder',
43 'fileFolder_extList',
44 'fileFolder_recursions',
45 'MM',
46 'MM_opposite_field',
47 'MM_match_fields',
48 'MM_insert_fields',
49 'MM_table_where',
50 'MM_hasUidField',
51 'special'
52 );
53
54 /**
55 * Modify the Data Structure of a FlexForm field via TSconfig and group access lists
56 *
57 * @param array $dataStructure The data structure of the FlexForm field
58 * @param string $table The table name of the record
59 * @param string $tableField The field name
60 * @param array $tableRow The record data
61 * @param array $tableConf Additional configuration options
62 * @return array Modified FlexForm DS
63 * @see \TYPO3\CMS\Backend\Form\FormEngine::getSingleField_typeFlex()
64 */
65 public function modifyFlexFormDS(array $dataStructure, $table, $tableField, array $tableRow, array $tableConf) {
66 $singleSheet = !isset($dataStructure['sheets']) || !is_array($dataStructure['sheets']);
67 $metaConf = !empty($dataStructure['meta']) ? $dataStructure['meta'] : array();
68 $sheetConf = array();
69 // Get extension identifier (uses second pointer field if it's value is not empty,
70 // "list" or "*", else it must be a plugin and first one will be used)
71 $pointerFields = !empty($tableConf['config']['ds_pointerField']) ? $tableConf['config']['ds_pointerField'] : 'list_type,CType';
72 $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
73 $flexformIdentifier = !empty($tableRow[$pointerFields[0]]) ? $tableRow[$pointerFields[0]] : '';
74 if (!empty($tableRow[$pointerFields[1]]) && $tableRow[$pointerFields[1]] != 'list' && $tableRow[$pointerFields[1]] != '*') {
75 $flexformIdentifier = $tableRow[$pointerFields[1]];
76 }
77 if (empty($flexformIdentifier)) {
78 return $dataStructure;
79 }
80 // Get field configuration from page TSConfig
81 $TSconfig = $this->setTSconfig($table, $tableRow);
82 if (!empty($TSconfig[$tableField][($flexformIdentifier . '.')])) {
83 $sheetConf = GeneralUtility::removeDotsFromTS($TSconfig[$tableField][$flexformIdentifier . '.']);
84 }
85 // Get non-exclude-fields from group access lists
86 $nonExcludeFields = $this->getFlexFormNonExcludeFields($table, $tableField, $flexformIdentifier);
87 // Load complete DS, including external file references
88 $dataStructure = GeneralUtility::resolveAllSheetsInDS($dataStructure);
89 // Modify language handling in meta configuration
90 if (isset($sheetConf['langDisable'])) {
91 $metaConf['langDisable'] = $sheetConf['langDisable'];
92 }
93 if (isset($sheetConf['langChildren'])) {
94 $metaConf['langChildren'] = $sheetConf['langChildren'];
95 }
96 // Modify flexform sheets
97 foreach ($dataStructure['sheets'] as $sheetName => $sheet) {
98 if (empty($sheet['ROOT']['el']) || !is_array($sheet['ROOT']['el'])) {
99 continue;
100 }
101 // Remove whole sheet (tab) if disabled
102 if (!empty($sheetConf[$sheetName]['disabled'])) {
103 unset($dataStructure['sheets'][$sheetName]);
104 continue;
105 }
106 // Rename sheet (tab)
107 if (!empty($sheetConf[$sheetName]['sheetTitle'])) {
108 $dataStructure['sheets'][$sheetName]['ROOT']['TCEforms']['sheetTitle'] = $sheetConf[$sheetName]['sheetTitle'];
109 }
110 // Set sheet description (tab)
111 if (!empty($sheetConf[$sheetName]['sheetDescription'])) {
112 $dataStructure['sheets'][$sheetName]['ROOT']['TCEforms']['sheetDescription'] = $sheetConf[$sheetName]['sheetDescription'];
113 }
114 // Set sheet short description (tab)
115 if (!empty($sheetConf[$sheetName]['sheetShortDescr'])) {
116 $dataStructure['sheets'][$sheetName]['ROOT']['TCEforms']['sheetShortDescr'] = $sheetConf[$sheetName]['sheetShortDescr'];
117 }
118 // Modify all configured fields in sheet (tab)
119 $dataStructure['sheets'][$sheetName]['ROOT']['el'] = $this->modifySingleFlexFormSheet($sheet['ROOT']['el'], $table, $tableField, $tableRow, !empty($sheetConf[$sheetName]) ? $sheetConf[$sheetName] : array(), !empty($nonExcludeFields[$sheetName]) ? $nonExcludeFields[$sheetName] : array());
120 // Remove empty sheet (tab)
121 if (empty($dataStructure['sheets'][$sheetName]['ROOT']['el'])) {
122 unset($dataStructure['sheets'][$sheetName]);
123 }
124 }
125 // Recover single flexform structure
126 if ($singleSheet && isset($dataStructure['sheets']['sDEF'])) {
127 $dataStructure = $dataStructure['sheets']['sDEF'];
128 }
129 // Recover meta configuration
130 if (!empty($metaConf)) {
131 $dataStructure['meta'] = $metaConf;
132 }
133 return $dataStructure;
134 }
135
136 /**
137 * Modify a single FlexForm sheet according to given configuration
138 *
139 * @param array $sheet Flexform sheet to manipulate
140 * @param string $table The table name
141 * @param string $tableField The field name
142 * @param array $tableRow The record data
143 * @param array $sheetConf Sheet configuration
144 * @param array $nonExcludeFields Non-exclude-fields for this sheet
145 * @return array Modified sheet
146 * @see \TYPO3\CMS\Backend\Form\FlexFormsHelper::modifyFlexFormDS()
147 */
148 public function modifySingleFlexFormSheet(array $sheet, $table, $tableField, array $tableRow, array $sheetConf, array $nonExcludeFields) {
149 if (empty($sheet) || empty($table) || empty($tableField) || empty($tableRow)) {
150 return $sheet;
151 }
152 // Modify fields
153 foreach ($sheet as $fieldName => $field) {
154 // Remove excluded fields
155 if (!$GLOBALS['BE_USER']->isAdmin() && !empty($field['TCEforms']['exclude']) && empty($nonExcludeFields[$fieldName])) {
156 unset($sheet[$fieldName]);
157 continue;
158 }
159 // Stop here if no TSConfig was found for this field
160 if (empty($sheetConf[$fieldName]) || !is_array($sheetConf[$fieldName])) {
161 continue;
162 }
163 // Remove disabled fields
164 if (!empty($sheetConf[$fieldName]['disabled'])) {
165 unset($sheet[$fieldName]);
166 continue;
167 }
168 $fieldConf = $sheetConf[$fieldName];
169 $removeItems = !empty($fieldConf['removeItems']) ? GeneralUtility::trimExplode(',', $fieldConf['removeItems'], TRUE) : array();
170 $keepItems = !empty($fieldConf['keepItems']) ? GeneralUtility::trimExplode(',', $fieldConf['keepItems'], TRUE) : array();
171 $renameItems = !empty($fieldConf['altLabels']) && is_array($fieldConf['altLabels']) ? $fieldConf['altLabels'] : array();
172 $changeIcons = !empty($fieldConf['altIcons']) && is_array($fieldConf['altIcons']) ? $fieldConf['altIcons'] : array();
173 $addItems = !empty($fieldConf['addItems']) && is_array($fieldConf['addItems']) ? $fieldConf['addItems'] : array();
174 unset($fieldConf['removeItems']);
175 unset($fieldConf['keepItems']);
176 unset($fieldConf['altLabels']);
177 unset($fieldConf['altIcons']);
178 unset($fieldConf['addItems']);
179 // Manipulate field
180 if (!empty($field['TCEforms']) && is_array($field['TCEforms'])) {
181 $sheet[$fieldName]['TCEforms'] = $field['TCEforms'];
182 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($sheet[$fieldName]['TCEforms'], $fieldConf);
183 }
184 // Manipulate only select fields, other field types will stop here
185 if (empty($field['TCEforms']['config']['type']) || $field['TCEforms']['config']['type'] != 'select' || $field['TCEforms']['config']['renderMode'] === 'tree') {
186 continue;
187 }
188 // Getting the selector box items from system
189 $selItems = $this->addSelectOptionsToItemArray($this->initItemArray($field['TCEforms']), $field['TCEforms'], $this->setTSconfig($table, $tableRow), $tableField);
190
191 // Possibly filter some items
192 $selItems = GeneralUtility::keepItemsInArray(
193 $selItems,
194 $keepItems,
195 function ($value) {
196 return $value[1];
197 }
198 );
199
200 // Possibly add some items
201 $selItems = $this->addItems($selItems, $addItems);
202 // Process items by a user function
203 if (!empty($field['TCEforms']['config']['itemsProcFunc'])) {
204 $selItems = $this->procItems($selItems, $fieldConf['config'], $field['TCEforms']['config'], $table, $tableRow, $tableField);
205 }
206 // Remove special configuration options after creating items to prevent double parsing
207 foreach ($this->removeSelectConfig as $option) {
208 unset($sheet[$fieldName]['TCEforms']['config'][$option]);
209 }
210 // Rename and remove items or change item icon in select
211 if ((!empty($removeItems) || !empty($renameItems) || !empty($changeIcons)) && !empty($selItems) && is_array($selItems)) {
212 foreach ($selItems as $itemKey => $itemConf) {
213 // Option has no key, no manipulation possible
214 if (!isset($itemConf[1])) {
215 continue;
216 }
217 // Remove
218 foreach ($removeItems as $removeKey => $removeValue) {
219 if (strcasecmp($removeValue, $itemConf[1]) == 0) {
220 unset($selItems[$itemKey]);
221 unset($removeItems[$removeKey]);
222 }
223 }
224 // Rename
225 foreach ($renameItems as $renameKey => $renameValue) {
226 if (strcasecmp($renameKey, $itemConf[1]) == 0) {
227 $selItems[$itemKey][0] = htmlspecialchars($renameValue);
228 unset($renameItems[$renameKey]);
229 }
230 }
231 // Change icon
232 foreach ($changeIcons as $iconKey => $iconValue) {
233 if (strcasecmp($iconKey, $itemConf[1]) == 0) {
234 $selItems[$itemKey][2] = $iconValue;
235 unset($changeIcons[$iconKey]);
236 }
237 }
238 }
239 }
240 $sheet[$fieldName]['TCEforms']['config']['items'] = $selItems;
241 }
242 return $sheet;
243 }
244
245 /**
246 * Get FlexForm non-exclude-fields for current backend user
247 *
248 * @param string $table The table name
249 * @param string $tableField The field name
250 * @param string $extIdent The extension identifier
251 * @return array All non_exclude_fields from FlexForms
252 * @see \TYPO3\CMS\Backend\Form\FormEngine::getSingleField_typeFlex()
253 */
254 protected function getFlexFormNonExcludeFields($table, $tableField, $extIdent) {
255 if (empty($GLOBALS['BE_USER']->groupData['non_exclude_fields']) || empty($table) || empty($tableField) || empty($extIdent)) {
256 return array();
257 }
258 $accessListFields = GeneralUtility::trimExplode(',', $GLOBALS['BE_USER']->groupData['non_exclude_fields']);
259 $identPrefix = $table . ':' . $tableField . ';' . $extIdent . ';';
260 $nonExcludeFields = array();
261 // Collect only FlexForm fields
262 foreach ($accessListFields as $field) {
263 if (strpos($field, $identPrefix) !== FALSE) {
264 list(, , $sheetName, $fieldName) = explode(';', $field);
265 $nonExcludeFields[$sheetName][$fieldName] = TRUE;
266 }
267 }
268 return $nonExcludeFields;
269 }
270
271 /**
272 * Compare two arrays by their first value
273 *
274 * @param array $array1 First array
275 * @param array $array2 Second array
276 * @return int Negative int if first array is lower, zero if both are identical, and positive if second is higher
277 */
278 static public function compareArraysByFirstValue(array $array1, array $array2) {
279 $array1 = reset($array1);
280 $array2 = reset($array2);
281 if (is_string($array1) && is_string($array2)) {
282 return strcasecmp($array1, $array2);
283 }
284 return 0;
285 }
286
287 }