[TASK] Re-work/simplify copyright header in PHP files - Part 2
[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
19 /**
20 * Contains functions for manipulating flex form data
21 *
22 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
23 */
24 class FlexFormTools {
25
26 // If set, the charset of data XML is converted to system charset.
27 /**
28 * @todo Define visibility
29 */
30 public $convertCharset = FALSE;
31
32 // If set, section indexes are re-numbered before processing
33 /**
34 * @todo Define visibility
35 */
36 public $reNumberIndexesOfSectionData = FALSE;
37
38 // Contains data structure when traversing flexform
39 /**
40 * @todo Define visibility
41 */
42 public $traverseFlexFormXMLData_DS = array();
43
44 // Contains data array when traversing flexform
45 /**
46 * @todo Define visibility
47 */
48 public $traverseFlexFormXMLData_Data = array();
49
50 // Options for array2xml() for flexform.
51 // This will map the weird keys from the internal array to tags that could potentially be checked with a DTD/schema
52 /**
53 * @todo Define visibility
54 */
55 public $flexArray2Xml_options = array(
56 'parentTagMap' => array(
57 'data' => 'sheet',
58 'sheet' => 'language',
59 'language' => 'field',
60 'el' => 'field',
61 'field' => 'value',
62 'field:el' => 'el',
63 'el:_IS_NUM' => 'section',
64 'section' => 'itemType'
65 ),
66 'disableTypeAttrib' => 2
67 );
68
69 /**
70 * Reference to object called
71 *
72 * @todo Define visibility
73 */
74 public $callBackObj = NULL;
75
76 // Used for accumulation of clean XML
77 /**
78 * @todo Define visibility
79 */
80 public $cleanFlexFormXML = array();
81
82 /**
83 * Handler for Flex Forms
84 *
85 * @param string $table The table name of the record
86 * @param string $field The field name of the flexform field to work on
87 * @param array $row The record data array
88 * @param object $callBackObj Object (passed by reference) in which the call back function is located
89 * @param string $callBackMethod_value Method name of call back function in object for values
90 * @return boolean If TRUE, error happened (error string returned)
91 * @todo Define visibility
92 */
93 public function traverseFlexFormXMLData($table, $field, $row, $callBackObj, $callBackMethod_value) {
94 if (!is_array($GLOBALS['TCA'][$table]) || !is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
95 return 'TCA table/field was not defined.';
96 }
97 $this->callBackObj = $callBackObj;
98 // Get Data Structure:
99 $dataStructArray = BackendUtility::getFlexFormDS($GLOBALS['TCA'][$table]['columns'][$field]['config'], $row, $table, $field);
100 // If data structure was ok, proceed:
101 if (is_array($dataStructArray)) {
102 // Get flexform XML data:
103 $xmlData = $row[$field];
104 // Convert charset:
105 if ($this->convertCharset) {
106 $xmlHeaderAttributes = \TYPO3\CMS\Core\Utility\GeneralUtility::xmlGetHeaderAttribs($xmlData);
107 $storeInCharset = strtolower($xmlHeaderAttributes['encoding']);
108 if ($storeInCharset) {
109 $currentCharset = $GLOBALS['LANG']->charSet;
110 $xmlData = $GLOBALS['LANG']->csConvObj->conv($xmlData, $storeInCharset, $currentCharset, 1);
111 }
112 }
113 $editData = \TYPO3\CMS\Core\Utility\GeneralUtility::xml2array($xmlData);
114 if (!is_array($editData)) {
115 return 'Parsing error: ' . $editData;
116 }
117 // Language settings:
118 $langChildren = $dataStructArray['meta']['langChildren'] ? 1 : 0;
119 $langDisabled = $dataStructArray['meta']['langDisable'] ? 1 : 0;
120 // Empty or invalid <meta>
121 if (!is_array($editData['meta'])) {
122 $editData['meta'] = array();
123 }
124 $editData['meta']['currentLangId'] = array();
125 $languages = $this->getAvailableLanguages();
126 foreach ($languages as $lInfo) {
127 $editData['meta']['currentLangId'][] = $lInfo['ISOcode'];
128 }
129 if (!count($editData['meta']['currentLangId'])) {
130 $editData['meta']['currentLangId'] = array('DEF');
131 }
132 $editData['meta']['currentLangId'] = array_unique($editData['meta']['currentLangId']);
133 if ($langChildren || $langDisabled) {
134 $lKeys = array('DEF');
135 } else {
136 $lKeys = $editData['meta']['currentLangId'];
137 }
138 // Tabs sheets
139 if (is_array($dataStructArray['sheets'])) {
140 $sKeys = array_keys($dataStructArray['sheets']);
141 } else {
142 $sKeys = array('sDEF');
143 }
144 // Traverse languages:
145 foreach ($lKeys as $lKey) {
146 foreach ($sKeys as $sheet) {
147 $sheetCfg = $dataStructArray['sheets'][$sheet];
148 list($dataStruct, $sheet) = \TYPO3\CMS\Core\Utility\GeneralUtility::resolveSheetDefInDS($dataStructArray, $sheet);
149 // Render sheet:
150 if (is_array($dataStruct['ROOT']) && is_array($dataStruct['ROOT']['el'])) {
151 // Separate language key
152 $lang = 'l' . $lKey;
153 $PA['vKeys'] = $langChildren && !$langDisabled ? $editData['meta']['currentLangId'] : array('DEF');
154 $PA['lKey'] = $lang;
155 $PA['callBackMethod_value'] = $callBackMethod_value;
156 $PA['table'] = $table;
157 $PA['field'] = $field;
158 $PA['uid'] = $row['uid'];
159 $this->traverseFlexFormXMLData_DS = &$dataStruct;
160 $this->traverseFlexFormXMLData_Data = &$editData;
161 // Render flexform:
162 $this->traverseFlexFormXMLData_recurse($dataStruct['ROOT']['el'], $editData['data'][$sheet][$lang], $PA, 'data/' . $sheet . '/' . $lang);
163 } else {
164 return 'Data Structure ERROR: No ROOT element found for sheet "' . $sheet . '".';
165 }
166 }
167 }
168 } else {
169 return 'Data Structure ERROR: ' . $dataStructArray;
170 }
171 }
172
173 /**
174 * Recursively traversing flexform data according to data structure and element data
175 *
176 * @param array $dataStruct (Part of) data structure array that applies to the sub section of the flexform data we are processing
177 * @param array $editData (Part of) edit data array, reflecting current part of data structure
178 * @param array $PA Additional parameters passed.
179 * @param string $path Telling the "path" to the element in the flexform XML
180 * @return array
181 * @todo Define visibility
182 */
183 public function traverseFlexFormXMLData_recurse($dataStruct, $editData, &$PA, $path = '') {
184 if (is_array($dataStruct)) {
185 foreach ($dataStruct as $key => $value) {
186 // The value of each entry must be an array.
187 if (is_array($value)) {
188 if ($value['type'] == 'array') {
189 // Array (Section) traversal
190 if ($value['section']) {
191 $cc = 0;
192 if (is_array($editData[$key]['el'])) {
193 if ($this->reNumberIndexesOfSectionData) {
194 $temp = array();
195 $c3 = 0;
196 foreach ($editData[$key]['el'] as $v3) {
197 $temp[++$c3] = $v3;
198 }
199 $editData[$key]['el'] = $temp;
200 }
201 foreach ($editData[$key]['el'] as $k3 => $v3) {
202 if (is_array($v3)) {
203 $cc = $k3;
204 $theType = key($v3);
205 $theDat = $v3[$theType];
206 $newSectionEl = $value['el'][$theType];
207 if (is_array($newSectionEl)) {
208 $this->traverseFlexFormXMLData_recurse(array($theType => $newSectionEl), array($theType => $theDat), $PA, $path . '/' . $key . '/el/' . $cc);
209 }
210 }
211 }
212 }
213 } else {
214 // Array traversal:
215 $this->traverseFlexFormXMLData_recurse($value['el'], $editData[$key]['el'], $PA, $path . '/' . $key . '/el');
216 }
217 } elseif (is_array($value['TCEforms']['config'])) {
218 // Processing a field value:
219 foreach ($PA['vKeys'] as $vKey) {
220 $vKey = 'v' . $vKey;
221 // Call back:
222 if ($PA['callBackMethod_value']) {
223 $this->callBackObj->{$PA['callBackMethod_value']}($value, $editData[$key][$vKey], $PA, $path . '/' . $key . '/' . $vKey, $this);
224 }
225 }
226 }
227 }
228 }
229 }
230 }
231
232 /**
233 * Returns an array of available languages to use for FlexForm operations
234 *
235 * @return array
236 * @todo Define visibility
237 */
238 public function getAvailableLanguages() {
239 $isL = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('static_info_tables');
240 // Find all language records in the system
241 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('static_lang_isocode,title,uid', 'sys_language', 'pid=0' . BackendUtility::deleteClause('sys_language'), '', 'title');
242 // Traverse them
243 $output = array();
244 $output[0] = array(
245 'uid' => 0,
246 'title' => 'Default language',
247 'ISOcode' => 'DEF'
248 );
249 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
250 $output[$row['uid']] = $row;
251 if ($isL && $row['static_lang_isocode']) {
252 $rr = BackendUtility::getRecord('static_languages', $row['static_lang_isocode'], 'lg_iso_2');
253 if ($rr['lg_iso_2']) {
254 $output[$row['uid']]['ISOcode'] = $rr['lg_iso_2'];
255 }
256 }
257 if (!$output[$row['uid']]['ISOcode']) {
258 unset($output[$row['uid']]);
259 }
260 }
261 $GLOBALS['TYPO3_DB']->sql_free_result($res);
262 return $output;
263 }
264
265 /***********************************
266 *
267 * Processing functions
268 *
269 ***********************************/
270 /**
271 * 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.
272 * 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.
273 *
274 * @param string $table Table name
275 * @param string $field Field name of the flex form field in which the XML is found that should be cleaned.
276 * @param array $row The record
277 * @return string Clean XML from FlexForm field
278 * @todo Define visibility
279 */
280 public function cleanFlexFormXML($table, $field, $row) {
281 // New structure:
282 $this->cleanFlexFormXML = array();
283 // Create and call iterator object:
284 $flexObj = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\FlexForm\\FlexFormTools');
285 $flexObj->reNumberIndexesOfSectionData = TRUE;
286 $flexObj->traverseFlexFormXMLData($table, $field, $row, $this, 'cleanFlexFormXML_callBackFunction');
287 return $this->flexArray2Xml($this->cleanFlexFormXML, TRUE);
288 }
289
290 /**
291 * Call back function for \TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools class
292 * Basically just setting the value in a new array (thus cleaning because only values that are valid are visited!)
293 *
294 * @param array $dsArr Data structure for the current value
295 * @param mixed $data Current value
296 * @param array $PA Additional configuration used in calling function
297 * @param string $path Path of value in DS structure
298 * @param object $pObj Object reference to caller
299 * @return void
300 * @todo Define visibility
301 */
302 public function cleanFlexFormXML_callBackFunction($dsArr, $data, $PA, $path, $pObj) {
303 // Just setting value in our own result array, basically replicating the structure:
304 $pObj->setArrayValueByPath($path, $this->cleanFlexFormXML, $data);
305 // Looking if an "extension" called ".vDEFbase" is found and if so, accept that too:
306 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['flexFormXMLincludeDiffBase']) {
307 $vDEFbase = $pObj->getArrayValueByPath($path . '.vDEFbase', $pObj->traverseFlexFormXMLData_Data);
308 if (isset($vDEFbase)) {
309 $pObj->setArrayValueByPath($path . '.vDEFbase', $this->cleanFlexFormXML, $vDEFbase);
310 }
311 }
312 }
313
314 /***********************************
315 *
316 * Multi purpose functions
317 *
318 ***********************************/
319 /**
320 * Get a value from a multi-dimensional array by giving a path "../../.." pointing to the element
321 *
322 * @param string $pathArray The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
323 * @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!
324 * @return mixed Value returned
325 * @todo Define visibility
326 */
327 public function &getArrayValueByPath($pathArray, &$array) {
328 if (!is_array($pathArray)) {
329 $pathArray = explode('/', $pathArray);
330 }
331 if (is_array($array)) {
332 if (count($pathArray)) {
333 $key = array_shift($pathArray);
334 if (isset($array[$key])) {
335 if (!count($pathArray)) {
336 return $array[$key];
337 } else {
338 return $this->getArrayValueByPath($pathArray, $array[$key]);
339 }
340 } else {
341 return NULL;
342 }
343 }
344 }
345 }
346
347 /**
348 * Set a value in a multi-dimensional array by giving a path "../../.." pointing to the element
349 *
350 * @param string $pathArray The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
351 * @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!
352 * @param mixed $value Value to set
353 * @return mixed Value returned
354 * @todo Define visibility
355 */
356 public function setArrayValueByPath($pathArray, &$array, $value) {
357 if (isset($value)) {
358 if (!is_array($pathArray)) {
359 $pathArray = explode('/', $pathArray);
360 }
361 if (is_array($array)) {
362 if (count($pathArray)) {
363 $key = array_shift($pathArray);
364 if (!count($pathArray)) {
365 $array[$key] = $value;
366 return TRUE;
367 } else {
368 if (!isset($array[$key])) {
369 $array[$key] = array();
370 }
371 return $this->setArrayValueByPath($pathArray, $array[$key], $value);
372 }
373 }
374 }
375 }
376 }
377
378 /**
379 * Convert FlexForm data array to XML
380 *
381 * @param array $array Array to output in <T3FlexForms> XML
382 * @param boolean $addPrologue If set, the XML prologue is returned as well.
383 * @return string XML content.
384 * @todo Define visibility
385 */
386 public function flexArray2Xml($array, $addPrologue = FALSE) {
387 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['flexformForceCDATA']) {
388 $this->flexArray2Xml_options['useCDATA'] = 1;
389 }
390 $options = $GLOBALS['TYPO3_CONF_VARS']['BE']['niceFlexFormXMLtags'] ? $this->flexArray2Xml_options : array();
391 $spaceInd = $GLOBALS['TYPO3_CONF_VARS']['BE']['compactFlexFormXML'] ? -1 : 4;
392 $output = \TYPO3\CMS\Core\Utility\GeneralUtility::array2xml($array, '', 0, 'T3FlexForms', $spaceInd, $options);
393 if ($addPrologue) {
394 $output = '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . LF . $output;
395 }
396 return $output;
397 }
398
399 }