2 /***************************************************************
5 * (c) 2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
28 * Contains functions for manipulating flex form data
32 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 * [CLASS/FUNCTION INDEX of SCRIPT]
39 * 71: class t3lib_flexformtools
40 * 105: function traverseFlexFormXMLData($table,$field,$row,&$callBackObj,$callBackMethod_value)
41 * 203: function traverseFlexFormXMLData_recurse($dataStruct,$editData,&$PA,$path='')
42 * 274: function getAvailableLanguages()
44 * SECTION: Processing functions
45 * 323: function cleanFlexFormXML($table,$field,$row)
46 * 347: function cleanFlexFormXML_callBackFunction($dsArr, $data, $PA, $path, &$pObj)
48 * SECTION: Multi purpose functions
49 * 374: function &getArrayValueByPath($pathArray,&$array)
50 * 403: function setArrayValueByPath($pathArray,&$array,$value)
51 * 433: function flexArray2Xml($array, $addPrologue=FALSE)
54 * (This index is automatically created/updated by the extension "extdeveval")
65 * Contains functions for manipulating flex form data
67 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
71 class t3lib_flexformtools
{
73 var $convertCharset = FALSE; // If set, the charset of data XML is converted to system charset.
74 var $reNumberIndexesOfSectionData = FALSE; // If set, section indexes are re-numbered before processing
76 var $traverseFlexFormXMLData_DS = array(); // Contains data structure when traversing flexform
77 var $traverseFlexFormXMLData_Data = array(); // Contains data array when traversing flexform
79 // Options for array2xml() for flexform. This will map the weird keys from the internal array to tags that could potentially be checked with a DTD/schema
80 var $flexArray2Xml_options = array(
81 'parentTagMap' => array(
83 'sheet' => 'language',
84 'language' => 'field',
88 'el:_IS_NUM' => 'section',
89 'section' => 'itemType'
91 'disableTypeAttrib' => 2
96 * Reference to object called
98 var $callBackObj = NULL;
99 var $cleanFlexFormXML = array(); // Used for accumulation of clean XML
102 * Handler for Flex Forms
104 * @param string The table name of the record
105 * @param string The field name of the flexform field to work on
106 * @param array The record data array
107 * @param object Object (passed by reference) in which the call back function is located
108 * @param string Method name of call back function in object for values
109 * @return boolean If true, error happened (error string returned)
111 function traverseFlexFormXMLData($table,$field,$row,&$callBackObj,$callBackMethod_value) {
113 if (!is_array($GLOBALS['TCA'][$table]) ||
!is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
114 return 'TCA table/field was not defined.';
117 $this->callBackObj
= &$callBackObj;
119 // Get Data Structure:
120 $dataStructArray = t3lib_BEfunc
::getFlexFormDS($GLOBALS['TCA'][$table]['columns'][$field]['config'],$row,$table);
122 // If data structure was ok, proceed:
123 if (is_array($dataStructArray)) {
125 // Get flexform XML data:
126 $xmlData = $row[$field];
129 if ($this->convertCharset
) {
130 $xmlHeaderAttributes = t3lib_div
::xmlGetHeaderAttribs($xmlData);
131 $storeInCharset = strtolower($xmlHeaderAttributes['encoding']);
132 if ($storeInCharset) {
133 $currentCharset = $GLOBALS['LANG']->charSet
;
134 $xmlData = $GLOBALS['LANG']->csConvObj
->conv($xmlData,$storeInCharset,$currentCharset,1);
138 $editData = t3lib_div
::xml2array($xmlData);
139 if (!is_array($editData)) {
140 return 'Parsing error: '.$editData;
143 // Language settings:
144 $langChildren = $dataStructArray['meta']['langChildren'] ?
1 : 0;
145 $langDisabled = $dataStructArray['meta']['langDisable'] ?
1 : 0;
147 // empty or invalid <meta>
148 if (!is_array($editData['meta'])) {
149 $editData['meta'] = array();
151 $editData['meta']['currentLangId'] = array();
152 $languages = $this->getAvailableLanguages();
154 foreach ($languages as $lInfo) {
155 $editData['meta']['currentLangId'][] = $lInfo['ISOcode'];
157 if (!count($editData['meta']['currentLangId'])) {
158 $editData['meta']['currentLangId'] = array('DEF');
160 $editData['meta']['currentLangId'] = array_unique($editData['meta']['currentLangId']);
162 if ($langChildren ||
$langDisabled) {
163 $lKeys = array('DEF');
165 $lKeys = $editData['meta']['currentLangId'];
169 if (is_array($dataStructArray['sheets'])) {
170 $sKeys = array_keys($dataStructArray['sheets']);
172 $sKeys = array('sDEF');
175 // Traverse languages:
176 foreach($lKeys as $lKey) {
177 foreach($sKeys as $sheet) {
178 $sheetCfg = $dataStructArray['sheets'][$sheet];
179 list ($dataStruct, $sheet) = t3lib_div
::resolveSheetDefInDS($dataStructArray,$sheet);
182 if (is_array($dataStruct['ROOT']) && is_array($dataStruct['ROOT']['el'])) {
183 $lang = 'l'.$lKey; // Separate language key
184 $PA['vKeys'] = $langChildren && !$langDisabled ?
$editData['meta']['currentLangId'] : array('DEF');
186 $PA['callBackMethod_value'] = $callBackMethod_value;
187 $PA['table'] = $table;
188 $PA['field'] = $field;
189 $PA['uid'] = $row['uid'];
191 $this->traverseFlexFormXMLData_DS
= &$dataStruct;
192 $this->traverseFlexFormXMLData_Data
= &$editData;
195 $this->traverseFlexFormXMLData_recurse(
196 $dataStruct['ROOT']['el'],
197 $editData['data'][$sheet][$lang],
199 'data/'.$sheet.'/'.$lang
201 } else return 'Data Structure ERROR: No ROOT element found for sheet "'.$sheet.'".';
204 } else return 'Data Structure ERROR: '.$dataStructArray;
208 * Recursively traversing flexform data according to data structure and element data
210 * @param array (Part of) data structure array that applies to the sub section of the flexform data we are processing
211 * @param array (Part of) edit data array, reflecting current part of data structure
212 * @param array Additional parameters passed.
213 * @param string Telling the "path" to the element in the flexform XML
216 function traverseFlexFormXMLData_recurse($dataStruct,$editData,&$PA,$path='') {
218 if (is_array($dataStruct)) {
219 foreach($dataStruct as $key => $value) {
220 if (is_array($value)) { // The value of each entry must be an array.
222 if ($value['type']=='array') {
223 if ($value['section']) { // Array (Section) traversal:
226 if (is_array($editData[$key]['el'])) {
228 if ($this->reNumberIndexesOfSectionData
) {
231 foreach($editData[$key]['el'] as $v3) {
234 $editData[$key]['el'] = $temp;
237 foreach($editData[$key]['el'] as $k3 => $v3) {
241 $theDat = $v3[$theType];
242 $newSectionEl = $value['el'][$theType];
243 if (is_array($newSectionEl)) {
244 $this->traverseFlexFormXMLData_recurse(
245 array($theType => $newSectionEl),
246 array($theType => $theDat),
248 $path.'/'.$key.'/el/'.$cc
254 } else { // Array traversal:
255 $this->traverseFlexFormXMLData_recurse(
257 $editData[$key]['el'],
262 } elseif (is_array($value['TCEforms']['config'])) { // Processing a field value:
264 foreach($PA['vKeys'] as $vKey) {
268 if ($PA['callBackMethod_value']) {
269 $this->callBackObj
->$PA['callBackMethod_value'](
271 $editData[$key][$vKey],
273 $path.'/'.$key.'/'.$vKey,
285 * Returns an array of available languages to use for FlexForm operations
289 function getAvailableLanguages() {
290 $isL = t3lib_extMgm
::isLoaded('static_info_tables');
292 // Find all language records in the system:
293 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('static_lang_isocode,title,uid', 'sys_language', 'pid=0'.t3lib_BEfunc
::deleteClause('sys_language'), '', 'title');
299 'title' => 'Default language',
303 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
304 $output[$row['uid']] = $row;
306 if ($isL && $row['static_lang_isocode']) {
307 $rr = t3lib_BEfunc
::getRecord('static_languages',$row['static_lang_isocode'],'lg_iso_2');
308 if ($rr['lg_iso_2']) $output[$row['uid']]['ISOcode']=$rr['lg_iso_2'];
311 if (!$output[$row['uid']]['ISOcode']) unset($output[$row['uid']]);
324 /***********************************
326 * Processing functions
328 ***********************************/
331 * 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.
332 * 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.
334 * @param string Table name
335 * @param string Field name of the flex form field in which the XML is found that should be cleaned.
336 * @param array The record
337 * @return string Clean XML from FlexForm field
339 function cleanFlexFormXML($table,$field,$row) {
342 $this->cleanFlexFormXML
= array();
344 // Create and call iterator object:
345 $flexObj = t3lib_div
::makeInstance('t3lib_flexformtools');
346 $flexObj->reNumberIndexesOfSectionData
= TRUE;
347 $flexObj->traverseFlexFormXMLData($table,$field,$row,$this,'cleanFlexFormXML_callBackFunction');
349 return $this->flexArray2Xml($this->cleanFlexFormXML
, TRUE);
353 * Call back function for t3lib_flexformtools class
354 * Basically just setting the value in a new array (thus cleaning because only values that are valid are visited!)
356 * @param array Data structure for the current value
357 * @param mixed Current value
358 * @param array Additional configuration used in calling function
359 * @param string Path of value in DS structure
360 * @param object Object reference to caller
363 function cleanFlexFormXML_callBackFunction($dsArr, $data, $PA, $path, &$pObj) {
364 #debug(array($dsArr, $data, $PA),$path);
365 // Just setting value in our own result array, basically replicating the structure:
366 $pObj->setArrayValueByPath($path,$this->cleanFlexFormXML
,$data);
377 /***********************************
379 * Multi purpose functions
381 ***********************************/
384 * Get a value from a multi-dimensional array by giving a path "../../.." pointing to the element
386 * @param string The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
387 * @param array Array to get value from. Passed by reference so the value returned can be used to change the value in the array!
388 * @return mixed Value returned
390 function &getArrayValueByPath($pathArray,&$array) {
391 if (!is_array($pathArray)) {
392 $pathArray = explode('/',$pathArray);
394 if (is_array($array)) {
395 if (count($pathArray)) {
396 $key = array_shift($pathArray);
398 if (isset($array[$key])) {
399 if (!count($pathArray)) {
402 return $this->getArrayValueByPath($pathArray,$array[$key]);
412 * Set a value in a multi-dimensional array by giving a path "../../.." pointing to the element
414 * @param string The path pointing to the value field, eg. test/2/title to access $array['test'][2]['title']
415 * @param array Array to set value in. Passed by reference so the value returned can be used to change the value in the array!
416 * @param mixed Value to set
417 * @return mixed Value returned
419 function setArrayValueByPath($pathArray,&$array,$value) {
421 if (!is_array($pathArray)) {
422 $pathArray = explode('/',$pathArray);
424 if (is_array($array)) {
425 if (count($pathArray)) {
426 $key = array_shift($pathArray);
428 if (!count($pathArray)) {
429 $array[$key] = $value;
432 if (!isset($array[$key])) {
433 $array[$key] = array();
435 return $this->setArrayValueByPath($pathArray,$array[$key],$value);
443 * Convert FlexForm data array to XML
445 * @param array Array to output in <T3FlexForms> XML
446 * @param boolean If set, the XML prologue is returned as well.
447 * @return string XML content.
449 function flexArray2Xml($array, $addPrologue=FALSE) {
451 $options = $GLOBALS['TYPO3_CONF_VARS']['BE']['niceFlexFormXMLtags'] ?
$this->flexArray2Xml_options
: array();
452 $spaceInd = ($GLOBALS['TYPO3_CONF_VARS']['BE']['compactFlexFormXML'] ?
-1 : 4);
453 $output = t3lib_div
::array2xml($array,'',0,'T3FlexForms', $spaceInd, $options);
456 $output = '<?xml version="1.0" encoding="'.$GLOBALS['LANG']->charSet
.'" standalone="yes" ?>'.chr(10).$output;
463 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_flexformtools.php']) {
464 include_once($TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_flexformtools.php']);