[!!!][TASK] Remove FlexForm options in DefaultConfiguration
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Configuration / FlexForm / FlexFormTools.php
1 <?php
2 namespace TYPO3\CMS\Core\Configuration\FlexForm;
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\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19
20 /**
21 * Contains functions for manipulating flex form data
22 */
23 class FlexFormTools
24 {
25 /**
26 * If set, section indexes are re-numbered before processing
27 *
28 * @var bool
29 */
30 public $reNumberIndexesOfSectionData = false;
31
32 /**
33 * Contains data structure when traversing flexform
34 *
35 * @var array
36 */
37 public $traverseFlexFormXMLData_DS = array();
38
39 /**
40 * Contains data array when traversing flexform
41 *
42 * @var array
43 */
44 public $traverseFlexFormXMLData_Data = array();
45
46 /**
47 * Options for array2xml() for flexform.
48 * This will map the weird keys from the internal array to tags that could potentially be checked with a DTD/schema
49 *
50 * @var array
51 */
52 public $flexArray2Xml_options = array(
53 'parentTagMap' => array(
54 'data' => 'sheet',
55 'sheet' => 'language',
56 'language' => 'field',
57 'el' => 'field',
58 'field' => 'value',
59 'field:el' => 'el',
60 'el:_IS_NUM' => 'section',
61 'section' => 'itemType'
62 ),
63 'disableTypeAttrib' => 2
64 );
65
66 /**
67 * Reference to object called
68 *
69 * @var object
70 */
71 public $callBackObj = null;
72
73 /**
74 * Used for accumulation of clean XML
75 *
76 * @var array
77 */
78 public $cleanFlexFormXML = array();
79
80 /**
81 * Handler for Flex Forms
82 *
83 * @param string $table The table name of the record
84 * @param string $field The field name of the flexform field to work on
85 * @param array $row The record data array
86 * @param object $callBackObj Object in which the call back function is located
87 * @param string $callBackMethod_value Method name of call back function in object for values
88 * @return bool|string If TRUE, error happened (error string returned)
89 */
90 public function traverseFlexFormXMLData($table, $field, $row, $callBackObj, $callBackMethod_value)
91 {
92 if (!is_array($GLOBALS['TCA'][$table]) || !is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
93 return 'TCA table/field was not defined.';
94 }
95 $this->callBackObj = $callBackObj;
96 // Get Data Structure:
97 $dataStructArray = BackendUtility::getFlexFormDS($GLOBALS['TCA'][$table]['columns'][$field]['config'], $row, $table, $field);
98 // If data structure was ok, proceed:
99 if (is_array($dataStructArray)) {
100 // Get flexform XML data
101 $editData = GeneralUtility::xml2array($row[$field]);
102 if (!is_array($editData)) {
103 return 'Parsing error: ' . $editData;
104 }
105 // Tabs sheets
106 if (is_array($dataStructArray['sheets'])) {
107 $sKeys = array_keys($dataStructArray['sheets']);
108 } else {
109 $sKeys = array('sDEF');
110 }
111 // Traverse languages:
112 foreach ($sKeys as $sheet) {
113 list($dataStruct, $sheet) = GeneralUtility::resolveSheetDefInDS($dataStructArray, $sheet);
114 // Render sheet:
115 if (is_array($dataStruct['ROOT']) && is_array($dataStruct['ROOT']['el'])) {
116 $PA['vKeys'] = array('DEF');
117 $PA['lKey'] = 'lDEF';
118 $PA['callBackMethod_value'] = $callBackMethod_value;
119 $PA['table'] = $table;
120 $PA['field'] = $field;
121 $PA['uid'] = $row['uid'];
122 $this->traverseFlexFormXMLData_DS = &$dataStruct;
123 $this->traverseFlexFormXMLData_Data = &$editData;
124 // Render flexform:
125 $this->traverseFlexFormXMLData_recurse($dataStruct['ROOT']['el'], $editData['data'][$sheet]['lDEF'], $PA, 'data/' . $sheet . '/lDEF');
126 } else {
127 return 'Data Structure ERROR: No ROOT element found for sheet "' . $sheet . '".';
128 }
129 }
130 } else {
131 return 'Data Structure ERROR: ' . $dataStructArray;
132 }
133 }
134
135 /**
136 * Recursively traversing flexform data according to data structure and element data
137 *
138 * @param array $dataStruct (Part of) data structure array that applies to the sub section of the flexform data we are processing
139 * @param array $editData (Part of) edit data array, reflecting current part of data structure
140 * @param array $PA Additional parameters passed.
141 * @param string $path Telling the "path" to the element in the flexform XML
142 * @return array
143 */
144 public function traverseFlexFormXMLData_recurse($dataStruct, $editData, &$PA, $path = '')
145 {
146 if (is_array($dataStruct)) {
147 foreach ($dataStruct as $key => $value) {
148 // The value of each entry must be an array.
149 if (is_array($value)) {
150 if ($value['type'] == 'array') {
151 // Array (Section) traversal
152 if ($value['section']) {
153 $cc = 0;
154 if (is_array($editData[$key]['el'])) {
155 if ($this->reNumberIndexesOfSectionData) {
156 $temp = array();
157 $c3 = 0;
158 foreach ($editData[$key]['el'] as $v3) {
159 $temp[++$c3] = $v3;
160 }
161 $editData[$key]['el'] = $temp;
162 }
163 foreach ($editData[$key]['el'] as $k3 => $v3) {
164 if (is_array($v3)) {
165 $cc = $k3;
166 $theType = key($v3);
167 $theDat = $v3[$theType];
168 $newSectionEl = $value['el'][$theType];
169 if (is_array($newSectionEl)) {
170 $this->traverseFlexFormXMLData_recurse(array($theType => $newSectionEl), array($theType => $theDat), $PA, $path . '/' . $key . '/el/' . $cc);
171 }
172 }
173 }
174 }
175 } else {
176 // Array traversal
177 if (is_array($editData) && is_array($editData[$key])) {
178 $this->traverseFlexFormXMLData_recurse($value['el'], $editData[$key]['el'], $PA, $path . '/' . $key . '/el');
179 }
180 }
181 } elseif (is_array($value['TCEforms']['config'])) {
182 // Processing a field value:
183 foreach ($PA['vKeys'] as $vKey) {
184 $vKey = 'v' . $vKey;
185 // Call back
186 if ($PA['callBackMethod_value'] && is_array($editData) && is_array($editData[$key])) {
187 $this->executeCallBackMethod($PA['callBackMethod_value'], array($value, $editData[$key][$vKey], $PA, $path . '/' . $key . '/' . $vKey, $this));
188 }
189 }
190 }
191 }
192 }
193 }
194 }
195
196 /**
197 * Execute method on callback object
198 *
199 * @param string $methodName Method name to call
200 * @param array $parameterArray Parameters
201 * @return mixed Result of callback object
202 */
203 protected function executeCallBackMethod($methodName, array $parameterArray)
204 {
205 return call_user_func_array(array($this->callBackObj, $methodName), $parameterArray);
206 }
207
208 /***********************************
209 *
210 * Processing functions
211 *
212 ***********************************/
213 /**
214 * Cleaning up FlexForm XML to hold only the values it may according to its Data Structure. Also the order of tags will follow that of the data structure.
215 * BE CAREFUL: DO not clean records in workspaces unless IN the workspace! The Data Structure might resolve falsely on a workspace record when cleaned from Live workspace.
216 *
217 * @param string $table Table name
218 * @param string $field Field name of the flex form field in which the XML is found that should be cleaned.
219 * @param array $row The record
220 * @return string Clean XML from FlexForm field
221 */
222 public function cleanFlexFormXML($table, $field, $row)
223 {
224 // New structure:
225 $this->cleanFlexFormXML = array();
226 // Create and call iterator object:
227 $flexObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools::class);
228 $flexObj->reNumberIndexesOfSectionData = true;
229 $flexObj->traverseFlexFormXMLData($table, $field, $row, $this, 'cleanFlexFormXML_callBackFunction');
230 return $this->flexArray2Xml($this->cleanFlexFormXML, true);
231 }
232
233 /**
234 * Call back function for \TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools class
235 * Basically just setting the value in a new array (thus cleaning because only values that are valid are visited!)
236 *
237 * @param array $dsArr Data structure for the current value
238 * @param mixed $data Current value
239 * @param array $PA Additional configuration used in calling function
240 * @param string $path Path of value in DS structure
241 * @param FlexFormTools $pObj caller
242 * @return void
243 */
244 public function cleanFlexFormXML_callBackFunction($dsArr, $data, $PA, $path, $pObj)
245 {
246 // Just setting value in our own result array, basically replicating the structure:
247 $pObj->setArrayValueByPath($path, $this->cleanFlexFormXML, $data);
248 }
249
250 /***********************************
251 *
252 * Multi purpose functions
253 *
254 ***********************************/
255 /**
256 * Get a value from a multi-dimensional array by giving a path "../../.." pointing to the element
257 *
258 * @param string $pathArray The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
259 * @param array $array Array to get value from. Passed by reference so the value returned can be used to change the value in the array!
260 * @return mixed Value returned
261 */
262 public function &getArrayValueByPath($pathArray, &$array)
263 {
264 if (!is_array($pathArray)) {
265 $pathArray = explode('/', $pathArray);
266 }
267 if (is_array($array) && !empty($pathArray)) {
268 $key = array_shift($pathArray);
269 if (isset($array[$key])) {
270 if (empty($pathArray)) {
271 return $array[$key];
272 }
273 return $this->getArrayValueByPath($pathArray, $array[$key]);
274 }
275 return null;
276 }
277 }
278
279 /**
280 * Set a value in a multi-dimensional array by giving a path "../../.." pointing to the element
281 *
282 * @param string $pathArray The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
283 * @param array $array Array to set value in. Passed by reference so the value returned can be used to change the value in the array!
284 * @param mixed $value Value to set
285 * @return mixed Value returned
286 */
287 public function setArrayValueByPath($pathArray, &$array, $value)
288 {
289 if (isset($value)) {
290 if (!is_array($pathArray)) {
291 $pathArray = explode('/', $pathArray);
292 }
293 if (is_array($array) && !empty($pathArray)) {
294 $key = array_shift($pathArray);
295 if (empty($pathArray)) {
296 $array[$key] = $value;
297 return true;
298 }
299 if (!isset($array[$key])) {
300 $array[$key] = array();
301 }
302 return $this->setArrayValueByPath($pathArray, $array[$key], $value);
303 }
304 }
305 }
306
307 /**
308 * Convert FlexForm data array to XML
309 *
310 * @param array $array Array to output in <T3FlexForms> XML
311 * @param bool $addPrologue If set, the XML prologue is returned as well.
312 * @return string XML content.
313 */
314 public function flexArray2Xml($array, $addPrologue = false)
315 {
316 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['flexformForceCDATA']) {
317 $this->flexArray2Xml_options['useCDATA'] = 1;
318 }
319 $output = GeneralUtility::array2xml($array, '', 0, 'T3FlexForms', 4, $this->flexArray2Xml_options);
320 if ($addPrologue) {
321 $output = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . LF . $output;
322 }
323 return $output;
324 }
325 }