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