[CLEANUP] Add phpDoc to properties in ext:backend
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / DataPreprocessor.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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Core\Utility\PathUtility;
20
21 /**
22 * Class for getting and transforming data for display in backend forms (TCEforms)
23 *
24 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
25 */
26 class DataPreprocessor {
27
28 /**
29 * If set, the records requested are locked.
30 *
31 * @var int
32 */
33 public $lockRecords = 0;
34
35 /**
36 * Is set externally if RTE is disabled.
37 *
38 * @var int
39 */
40 public $disableRTE = 0;
41
42 /**
43 * If the pid in the command is 'prev' then $prevPageID is used as pid for the record.
44 * This is used to attach new records to other previous records eg. new pages.
45 *
46 * @var string
47 */
48 public $prevPageID = '';
49
50 /**
51 * Can be set with an array of default values for tables. First key is table name,
52 * second level keys are field names. Originally this was a GLOBAL array used internally.
53 *
54 * @var array
55 */
56 public $defVals = array();
57
58 /**
59 * If set, the processed data is overlaid the raw record.
60 *
61 * @var bool
62 */
63 public $addRawData = FALSE;
64
65 /**
66 * Used to register, which items are already loaded!!
67 *
68 * @var array
69 */
70 public $regTableItems = array();
71
72 /**
73 * This stores the record data of the loaded records
74 *
75 * @var array
76 */
77 public $regTableItems_data = array();
78
79 /**
80 * Contains loadModules object, if used. (for reuse internally)
81 *
82 * @var string
83 */
84 public $loadModules = '';
85
86 /***********************************************
87 *
88 * Getting record content, ready for display in TCEforms
89 *
90 ***********************************************/
91 /**
92 * A function which can be used for load a batch of records from $table into internal memory of this object.
93 * The function is also used to produce proper default data for new records
94 * Ultimately the function will call renderRecord()
95 *
96 * @param string $table Table name, must be found in $GLOBALS['TCA']
97 * @param string $idList Comma list of id values. If $idList is "prev" then the value from $this->prevPageID is used. NOTICE: If $operation is "new", then negative ids are meant to point to a "previous" record and positive ids are PID values for new records. Otherwise (for existing records that is) it is straight forward table/id pairs.
98 * @param string $operation If "new", then a record with default data is returned. Further, the $id values are meant to be PID values (or if negative, pointing to a previous record). If NOT new, then the table/ids are just pointing to an existing record!
99 * @return void
100 * @see renderRecord()
101 */
102 public function fetchRecord($table, $idList, $operation) {
103 if ((string)$idList === 'prev') {
104 $idList = $this->prevPageID;
105 }
106 if ($GLOBALS['TCA'][$table]) {
107 // For each ID value (int) we
108 $ids = GeneralUtility::trimExplode(',', $idList, TRUE);
109 foreach ($ids as $id) {
110 // If ID is not blank:
111 if ((string)$id !== '') {
112 // For new records to be created, find default values:
113 if ($operation == 'new') {
114 // Default values:
115 // Used to store default values as found here:
116 $newRow = array();
117 // Default values as set in userTS:
118 $TCAdefaultOverride = $GLOBALS['BE_USER']->getTSConfigProp('TCAdefaults');
119 if (is_array($TCAdefaultOverride[$table . '.'])) {
120 foreach ($TCAdefaultOverride[$table . '.'] as $theF => $theV) {
121 if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) {
122 $newRow[$theF] = $theV;
123 }
124 }
125 }
126 if ($id < 0) {
127 $record = BackendUtility::getRecord($table, abs($id), 'pid');
128 $pid = $record['pid'];
129 unset($record);
130 } else {
131 $pid = (int)$id;
132 }
133 $pageTS = BackendUtility::getPagesTSconfig($pid);
134 if (isset($pageTS['TCAdefaults.'])) {
135 $TCAPageTSOverride = $pageTS['TCAdefaults.'];
136 if (is_array($TCAPageTSOverride[$table . '.'])) {
137 foreach ($TCAPageTSOverride[$table . '.'] as $theF => $theV) {
138 if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) {
139 $newRow[$theF] = $theV;
140 }
141 }
142 }
143 }
144 // Default values as submitted:
145 if (!empty($this->defVals[$table]) && is_array($this->defVals[$table])) {
146 foreach ($this->defVals[$table] as $theF => $theV) {
147 if (isset($GLOBALS['TCA'][$table]['columns'][$theF])) {
148 $newRow[$theF] = $theV;
149 }
150 }
151 }
152 // Fetch default values if a previous record exists
153 if ($id < 0 && $GLOBALS['TCA'][$table]['ctrl']['useColumnsForDefaultValues']) {
154 // Fetches the previous record:
155 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . abs($id) . BackendUtility::deleteClause($table));
156 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
157 // Gets the list of fields to copy from the previous record.
158 $fArr = GeneralUtility::trimExplode(',', $GLOBALS['TCA'][$table]['ctrl']['useColumnsForDefaultValues'], TRUE);
159 foreach ($fArr as $theF) {
160 if (isset($GLOBALS['TCA'][$table]['columns'][$theF]) && !isset($newRow[$theF])) {
161 $newRow[$theF] = $row[$theF];
162 }
163 }
164 }
165 $GLOBALS['TYPO3_DB']->sql_free_result($res);
166 }
167 // Finally, call renderRecord:
168 $this->renderRecord($table, uniqid('NEW', TRUE), $id, $newRow);
169 } else {
170 $id = (int)$id;
171 // Fetch database values
172 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . $id . BackendUtility::deleteClause($table));
173 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
174 BackendUtility::fixVersioningPid($table, $row);
175 $this->renderRecord($table, $id, $row['pid'], $row);
176 $this->lockRecord($table, $id, $table === 'tt_content' ? $row['pid'] : 0);
177 }
178 $GLOBALS['TYPO3_DB']->sql_free_result($res);
179 }
180 }
181 }
182 }
183 }
184
185 /**
186 * This function performs processing on the input $row array and stores internally a corresponding array which contains processed values, ready to pass on to the TCEforms rendering in the frontend!
187 * The objective with this function is to prepare the content for handling in TCEforms.
188 * Default values from outside/TSconfig is added by fetchRecord(). In this function default values from TCA is used if a field is NOT defined in $row.
189 * The resulting, processed row is stored in $this->regTableItems_data[$uniqueItemRef], where $uniqueItemRef is "[tablename]_[id-value]"
190 *
191 * @param string $table The table name
192 * @param string $id The uid value of the record (int). Can also be a string (NEW-something) if the record is a NEW record.
193 * @param int $pid The pid integer. For existing records this is of course the row's "pid" field. For new records it can be either a page id (positive) or a pointer to another record from the SAME table (negative) after which the record should be inserted (or on same page)
194 * @param array $row The row of the current record. If NEW record, then it may be loaded with default values (by eg. fetchRecord()).
195 * @return void
196 * @see fetchRecord()
197 */
198 public function renderRecord($table, $id, $pid, $row) {
199 $dateTimeFormats = $GLOBALS['TYPO3_DB']->getDateTimeFormats($table);
200 foreach ($GLOBALS['TCA'][$table]['columns'] as $column => $config) {
201 if (isset($config['config']['dbType']) && GeneralUtility::inList('date,datetime', $config['config']['dbType'])) {
202 $emptyValue = $dateTimeFormats[$config['config']['dbType']]['empty'];
203 $row[$column] = !empty($row[$column]) && $row[$column] !== $emptyValue ? strtotime($row[$column]) : 0;
204 }
205 }
206 // Init:
207 $uniqueItemRef = $table . '_' . $id;
208 // Fetches the true PAGE TSconfig pid to use later, if needed. (Until now, only for the RTE, but later..., who knows?)
209 list($tscPID) = BackendUtility::getTSCpid($table, $id, $pid);
210 $TSconfig = BackendUtility::getTCEFORM_TSconfig($table, array_merge($row, array('uid' => $id, 'pid' => $pid)));
211 // If the record has not already been loaded (in which case we DON'T do it again)...
212 if (!$this->regTableItems[$uniqueItemRef]) {
213 $this->regTableItems[$uniqueItemRef] = 1;
214 // set "loaded" flag.
215 // If the table is pages, set the previous page id internally.
216 if ($table == 'pages') {
217 $this->prevPageID = $id;
218 }
219 $this->regTableItems_data[$uniqueItemRef] = $this->renderRecordRaw($table, $id, $pid, $row, $TSconfig, $tscPID);
220 // Merges the processed array on-top of the raw one - this is done because some things in TCEforms may need access to other fields than those in the columns configuration!
221 if ($this->addRawData && is_array($row) && is_array($this->regTableItems_data[$uniqueItemRef])) {
222 $this->regTableItems_data[$uniqueItemRef] = array_merge($row, $this->regTableItems_data[$uniqueItemRef]);
223 }
224 }
225 }
226
227 /**
228 * This function performs processing on the input $row array and stores internally a corresponding array which contains processed values, ready to pass on to the TCEforms rendering in the frontend!
229 * The objective with this function is to prepare the content for handling in TCEforms.
230 * In opposite to renderRecord() this function do not prepare things like fetching TSconfig and others.
231 * The resulting, processed row will be returned.
232 *
233 * @param string $table The table name
234 * @param string $id The uid value of the record (int). Can also be a string (NEW-something) if the record is a NEW record.
235 * @param int $pid The pid integer. For existing records this is of course the row's "pid" field. For new records it can be either a page id (positive) or a pointer to another record from the SAME table (negative) after which the record should be inserted (or on same page)
236 * @param array $row The row of the current record. If NEW record, then it may be loaded with default values (by eg. fetchRecord()).
237 * @param array $TSconfig Tsconfig array
238 * @param int $tscPID PAGE TSconfig pid
239 * @return array Processed record data
240 * @see renderRecord()
241 */
242 public function renderRecordRaw($table, $id, $pid, $row, $TSconfig = '', $tscPID = 0) {
243 if (!is_array($TSconfig)) {
244 $TSconfig = array();
245 }
246 // Create blank accumulation array:
247 $totalRecordContent = array();
248 // Traverse the configured columns for the table (TCA):
249 // For each column configured, we will perform processing if needed based on the type (eg. for "group" and "select" types this is needed)
250 $copyOfColumns = $GLOBALS['TCA'][$table]['columns'];
251 foreach ($copyOfColumns as $field => $fieldConfig) {
252 // Set $data variable for the field, either inputted value from $row - or if not found, the default value as defined in the "config" array
253 if (isset($row[$field])) {
254 $data = (string)$row[$field];
255 } elseif (!empty($fieldConfig['config']['eval']) && GeneralUtility::inList($fieldConfig['config']['eval'], 'null')) {
256 // Field exists but is set to NULL
257 if (array_key_exists($field, $row)) {
258 $data = NULL;
259 // Only use NULL if default value was explicitly set to be backward compatible.
260 } elseif (array_key_exists('default', $fieldConfig['config']) && $fieldConfig['config']['default'] === NULL) {
261 $data = NULL;
262 } else {
263 $data = (string)$fieldConfig['config']['default'];
264 }
265 } else {
266 $data = (string)$fieldConfig['config']['default'];
267 }
268 $data = $this->renderRecord_SW($data, $fieldConfig, $TSconfig, $table, $row, $field);
269 $totalRecordContent[$field] = $data;
270 }
271 // Further processing may apply for each field in the record depending on the settings in the "types" configuration (the list of fields to currently display for a record in TCEforms).
272 // For instance this could be processing instructions for the Rich Text Editor.
273 $types_fieldConfig = BackendUtility::getTCAtypes($table, $totalRecordContent);
274 if (is_array($types_fieldConfig)) {
275 $totalRecordContent = $this->renderRecord_typesProc($totalRecordContent, $types_fieldConfig, $tscPID, $table, $pid);
276 }
277 // Register items, mostly for external use (overriding the regItem() function)
278 foreach ($totalRecordContent as $field => $data) {
279 $this->regItem($table, $id, $field, $data);
280 }
281 // Finally, store the result:
282 reset($totalRecordContent);
283 return $totalRecordContent;
284 }
285
286 /**
287 * Function with the switch() construct which triggers functions for processing of the data value depending on the TCA-config field type.
288 *
289 * @param string $data Value to process
290 * @param array $fieldConfig TCA/columns array for field (independant of TCA for flexforms - coming from XML then)
291 * @param array $TSconfig TSconfig (blank for flexforms for now)
292 * @param string $table Table name
293 * @param array $row The row array, always of the real record (also for flexforms)
294 * @param string $field The field
295 * @return string Modified $value
296 */
297 public function renderRecord_SW($data, $fieldConfig, $TSconfig, $table, $row, $field) {
298 switch ((string)$fieldConfig['config']['type']) {
299 case 'group':
300 $data = $this->renderRecord_groupProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
301 break;
302 case 'select':
303 $data = $this->renderRecord_selectProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
304 break;
305 case 'flex':
306 $data = $this->renderRecord_flexProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
307 break;
308 case 'inline':
309 $data = $this->renderRecord_inlineProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
310 break;
311 }
312 return $data;
313 }
314
315 /**
316 * Processing of the data value in case the field type is "group"
317 *
318 * @param string $data The field value
319 * @param array $fieldConfig TCA field config
320 * @param array $TSconfig TCEform TSconfig for the record
321 * @param string $table Table name
322 * @param array $row The row
323 * @param string $field Field name
324 * @return string The processed input field value ($data)
325 * @access private
326 * @see renderRecord()
327 */
328 public function renderRecord_groupProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
329 switch ($fieldConfig['config']['internal_type']) {
330 case 'file_reference':
331 case 'file':
332 // Init array used to accumulate the files:
333 $dataAcc = array();
334 // Now, load the files into the $dataAcc array, whether stored by MM or as a list of filenames:
335 if ($fieldConfig['config']['MM']) {
336 $loadDB = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
337 $loadDB->start('', 'files', $fieldConfig['config']['MM'], $row['uid']);
338 // Setting dummy startup
339 foreach ($loadDB->itemArray as $value) {
340 if ($value['id']) {
341 $dataAcc[] = rawurlencode($value['id']) . '|' . rawurlencode(PathUtility::basename($value['id']));
342 }
343 }
344 } else {
345 $fileList = GeneralUtility::trimExplode(',', $data, TRUE);
346 foreach ($fileList as $value) {
347 if ($value) {
348 $dataAcc[] = rawurlencode($value) . '|' . rawurlencode(PathUtility::basename($value));
349 }
350 }
351 }
352 // Implode the accumulation array to a comma separated string:
353 $data = implode(',', $dataAcc);
354 break;
355 case 'db':
356 $loadDB = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
357 /** @var $loadDB \TYPO3\CMS\Core\Database\RelationHandler */
358 $loadDB->start($data, $fieldConfig['config']['allowed'], $fieldConfig['config']['MM'], $row['uid'], $table, $fieldConfig['config']);
359 $loadDB->getFromDB();
360 $data = $loadDB->readyForInterface();
361 break;
362 }
363 return $data;
364 }
365
366 /**
367 * Processing of the data value in case the field type is "select"
368 *
369 * @param string $data The field value
370 * @param array $fieldConfig TCA field config
371 * @param array $TSconfig TCEform TSconfig for the record
372 * @param string $table Table name
373 * @param array $row The row
374 * @param string $field Field name
375 * @return string The processed input field value ($data)
376 * @access private
377 * @see renderRecord()
378 */
379 public function renderRecord_selectProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
380 // Initialize:
381 // Current data set.
382 $elements = GeneralUtility::trimExplode(',', $data, TRUE);
383 // New data set, ready for interface (list of values, rawurlencoded)
384 $dataAcc = array();
385 // For list selectors (multi-value):
386 if ((int)$fieldConfig['config']['maxitems'] > 1 || $fieldConfig['config']['renderMode'] === 'tree') {
387 // Add regular elements:
388 if (!is_array($fieldConfig['config']['items'])) {
389 $fieldConfig['config']['items'] = array();
390 }
391 $fieldConfig['config']['items'] = $this->procesItemArray($fieldConfig['config']['items'], $fieldConfig['config'], $TSconfig[$field], $table, $row, $field);
392 foreach ($fieldConfig['config']['items'] as $pvpv) {
393 foreach ($elements as $eKey => $value) {
394 if ((string)$value === (string)$pvpv[1]) {
395 $dataAcc[$eKey] = rawurlencode($pvpv[1]) . '|' . rawurlencode($this->sL($pvpv[0]));
396 }
397 }
398 }
399 // Add "special"
400 if ($fieldConfig['config']['special']) {
401 $dataAcc = $this->selectAddSpecial($dataAcc, $elements, $fieldConfig['config']['special']);
402 }
403 // Add "foreign table" stuff:
404 if ($GLOBALS['TCA'][$fieldConfig['config']['foreign_table']]) {
405 $dataAcc = $this->selectAddForeign($dataAcc, $elements, $fieldConfig, $field, $TSconfig, $row, $table);
406 }
407 // Always keep the native order for display in interface:
408 ksort($dataAcc);
409 } else {
410 // Normal, <= 1 -> value without title on it
411 if ($GLOBALS['TCA'][$fieldConfig['config']['foreign_table']]) {
412 // Getting the data
413 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
414 if (!count($dataIds)) {
415 $dataIds = array(0);
416 }
417 $dataAcc[] = $dataIds[0];
418 } else {
419 $dataAcc[] = $elements[0];
420 }
421 }
422 return implode(',', $dataAcc);
423 }
424
425 /**
426 * Processing of the data value in case the field type is "flex"
427 * MUST NOT be called in case of already INSIDE a flexform!
428 *
429 * @param string $data The field value
430 * @param array $fieldConfig CA field config
431 * @param array $TSconfig TCEform TSconfig for the record
432 * @param string $table Table name
433 * @param array $row The row
434 * @param string $field Field name
435 * @return string The processed input field value ($data)
436 * @access private
437 * @see renderRecord()
438 */
439 public function renderRecord_flexProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
440 // Convert the XML data to PHP array:
441 if (!is_array($data)) {
442 $currentValueArray = GeneralUtility::xml2array($data);
443 } else {
444 $currentValueArray = $data;
445 }
446 if (is_array($currentValueArray)) {
447 // Get current value array:
448 $dataStructArray = BackendUtility::getFlexFormDS($fieldConfig['config'], $row, $table, $field);
449 // Manipulate Flexform DS via TSConfig and group access lists
450 if (is_array($dataStructArray)) {
451 $flexFormHelper = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Form\\FlexFormsHelper');
452 $dataStructArray = $flexFormHelper->modifyFlexFormDS($dataStructArray, $table, $field, $row, $fieldConfig);
453 unset($flexFormHelper);
454 }
455 if (is_array($dataStructArray)) {
456 $currentValueArray['data'] = $this->renderRecord_flexProc_procInData($currentValueArray['data'], $dataStructArray, array($data, $fieldConfig, $TSconfig, $table, $row, $field));
457 $flexObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Configuration\\FlexForm\\FlexFormTools');
458 $data = $flexObj->flexArray2Xml($currentValueArray, TRUE);
459 }
460 }
461 return $data;
462 }
463
464 /**
465 * Processing of the content in $totalRecordcontent based on settings in the types-configuration
466 *
467 * @param array $totalRecordContent The array of values which has been processed according to their type (eg. "group" or "select")
468 * @param array $types_fieldConfig The "types" configuration for the current display of fields.
469 * @param int $tscPID PAGE TSconfig PID
470 * @param string $table Table name
471 * @param int $pid PID value
472 * @return array The processed version of $totalRecordContent
473 * @access private
474 */
475 public function renderRecord_typesProc($totalRecordContent, $types_fieldConfig, $tscPID, $table, $pid) {
476 foreach ($types_fieldConfig as $vconf) {
477 // Find file to write to, if configured:
478 $eFile = \TYPO3\CMS\Core\Html\RteHtmlParser::evalWriteFile($vconf['spec']['static_write'], $totalRecordContent);
479 // Write file configuration:
480 if (is_array($eFile)) {
481 if ($eFile['loadFromFileField'] && $totalRecordContent[$eFile['loadFromFileField']]) {
482 // Read the external file, and insert the content between the ###TYPO3_STATICFILE_EDIT### markers:
483 $SW_fileContent = GeneralUtility::getUrl($eFile['editFile']);
484 $parseHTML = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Html\\RteHtmlParser');
485 $parseHTML->init('', '');
486 $totalRecordContent[$vconf['field']] = $parseHTML->getSubpart($SW_fileContent, $eFile['markerField'] && trim($totalRecordContent[$eFile['markerField']]) ? trim($totalRecordContent[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###');
487 }
488 }
489 }
490 return $totalRecordContent;
491 }
492
493 /**
494 * Processing of the data value in case the field type is "inline"
495 * In some parts nearly the same as type "select"
496 *
497 * @param string $data The field value
498 * @param array $fieldConfig TCA field config
499 * @param array $TSconfig TCEform TSconfig for the record
500 * @param string $table Table name
501 * @param array $row The row
502 * @param string $field Field name
503 * @return string The processed input field value ($data)
504 * @access private
505 * @see renderRecord()
506 */
507 public function renderRecord_inlineProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
508 // Initialize:
509 // Current data set.
510 $elements = GeneralUtility::trimExplode(',', $data);
511 // New data set, ready for interface (list of values, rawurlencoded)
512 $dataAcc = array();
513 // At this point all records that CAN be selected is found in $recordList
514 // Now, get the data from loadDBgroup based on the input list of values.
515 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
516 // After this we can traverse the loadDBgroup values and match values with the list of possible values in $recordList:
517 foreach ($dataIds as $theId) {
518 if ($fieldConfig['config']['MM'] || $fieldConfig['config']['foreign_field']) {
519 $dataAcc[] = $theId;
520 } else {
521 foreach ($elements as $eKey => $value) {
522 if ((int)$theId === (int)$value) {
523 $dataAcc[$eKey] = $theId;
524 }
525 }
526 }
527 }
528 return implode(',', $dataAcc);
529 }
530
531 /***********************************************
532 *
533 * FlexForm processing functions
534 *
535 ***********************************************/
536 /**
537 * Function traversing sheets/languages for flex form data structures
538 *
539 * @param array $dataPart Data array
540 * @param array $dataStructArray Data Structure array
541 * @param array $pParams Various parameters to pass-through
542 * @return array Modified $dataPart array.
543 * @access private
544 * @see \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_flex_procInData(), renderRecord_flexProc_procInData_travDS()
545 */
546 public function renderRecord_flexProc_procInData($dataPart, $dataStructArray, $pParams) {
547 if (is_array($dataPart)) {
548 foreach ($dataPart as $sKey => $sheetDef) {
549 list($dataStruct, $actualSheet) = GeneralUtility::resolveSheetDefInDS($dataStructArray, $sKey);
550 if (is_array($dataStruct) && $actualSheet == $sKey && is_array($sheetDef)) {
551 foreach ($sheetDef as $lKey => $lData) {
552 $this->renderRecord_flexProc_procInData_travDS($dataPart[$sKey][$lKey], $dataStruct['ROOT']['el'], $pParams);
553 }
554 }
555 }
556 }
557 return $dataPart;
558 }
559
560 /**
561 * Traverse data array / structure
562 *
563 * @param array $dataValues Data array passed by reference.
564 * @param array $DSelements Data structure
565 * @param array $pParams Various parameters pass-through.
566 * @return void
567 * @see \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_flex_procInData(), renderRecord_flexProc_procInData_travDS()
568 */
569 public function renderRecord_flexProc_procInData_travDS(&$dataValues, $DSelements, $pParams) {
570 if (is_array($DSelements)) {
571 // For each DS element:
572 foreach ($DSelements as $key => $dsConf) {
573 // Array/Section:
574 if ($DSelements[$key]['type'] == 'array') {
575 if (is_array($dataValues[$key]['el'])) {
576 if ($DSelements[$key]['section']) {
577 foreach ($dataValues[$key]['el'] as $ik => $el) {
578 if (is_array($el)) {
579 $theKey = key($el);
580 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) {
581 $this->renderRecord_flexProc_procInData_travDS($dataValues[$key]['el'][$ik][$theKey]['el'], $DSelements[$key]['el'][$theKey]['el'], $pParams);
582 }
583 }
584 }
585 } else {
586 if (!isset($dataValues[$key]['el'])) {
587 $dataValues[$key]['el'] = array();
588 }
589 $this->renderRecord_flexProc_procInData_travDS($dataValues[$key]['el'], $DSelements[$key]['el'], $pParams);
590 }
591 }
592 } else {
593 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) {
594 foreach ($dataValues[$key] as $vKey => $data) {
595 // $data,$fieldConfig,$TSconfig,$table,$row,$field
596 list(, , $CVTSconfig, $CVtable, $CVrow, $CVfield) = $pParams;
597 // Set default value:
598 if (!isset($dataValues[$key][$vKey])) {
599 $dataValues[$key][$vKey] = $dsConf['TCEforms']['config']['default'];
600 }
601 // Process value:
602 $dataValues[$key][$vKey] = $this->renderRecord_SW($dataValues[$key][$vKey], $dsConf['TCEforms'], $CVTSconfig, $CVtable, $CVrow, $CVfield);
603 }
604 }
605 }
606 }
607 }
608 }
609
610 /***********************************************
611 *
612 * Selector box processing functions
613 *
614 ***********************************************/
615 /**
616 * Adding "special" types to the $dataAcc array of selector items
617 *
618 * @param array $dataAcc Array with numeric keys, containing values for the selector box, prepared for interface. We are going to add elements to this array as needed.
619 * @param array $elements The array of original elements - basically the field value exploded by ",
620 * @param string $specialKey The "special" key from the TCA config of the field. Determines the type of processing in here.
621 * @return array Modified $dataAcc array
622 * @access private
623 * @see renderRecord_selectProc()
624 */
625 public function selectAddSpecial($dataAcc, $elements, $specialKey) {
626 // Special select types:
627 switch ((string)$specialKey) {
628 case 'tables':
629 $tNames = array_keys($GLOBALS['TCA']);
630 foreach ($tNames as $tableName) {
631 foreach ($elements as $eKey => $value) {
632 if ((string)$tableName === (string)$value) {
633 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($GLOBALS['TCA'][$value]['ctrl']['title']));
634 }
635 }
636 }
637 break;
638 case 'pagetypes':
639 $theTypes = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
640 if (is_array($theTypes)) {
641 foreach ($theTypes as $theTypesArrays) {
642 foreach ($elements as $eKey => $value) {
643 if ((string)$theTypesArrays[1] === (string)$value) {
644 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($theTypesArrays[0]));
645 }
646 }
647 }
648 }
649 break;
650 case 'exclude':
651 $theExcludeFields = BackendUtility::getExcludeFields();
652 if (is_array($theExcludeFields)) {
653 foreach ($theExcludeFields as $theExcludeFieldsArrays) {
654 foreach ($elements as $eKey => $value) {
655 if ((string)$theExcludeFieldsArrays[1] === (string)$value) {
656 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode(rtrim($theExcludeFieldsArrays[0], ':'));
657 }
658 }
659 }
660 }
661 break;
662 case 'explicitValues':
663 $theTypes = BackendUtility::getExplicitAuthFieldValues();
664 foreach ($theTypes as $tableFieldKey => $theTypeArrays) {
665 if (is_array($theTypeArrays['items'])) {
666 foreach ($theTypeArrays['items'] as $itemValue => $itemContent) {
667 foreach ($elements as $eKey => $value) {
668 if (($tableFieldKey . ':' . $itemValue . ':' . $itemContent[0]) === (string)$value) {
669 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode(('[' . $itemContent[2] . '] ' . $itemContent[1]));
670 }
671 }
672 }
673 }
674 }
675 break;
676 case 'languages':
677 $theLangs = BackendUtility::getSystemLanguages();
678 foreach ($theLangs as $lCfg) {
679 foreach ($elements as $eKey => $value) {
680 if ((string)$lCfg[1] === (string)$value) {
681 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($lCfg[0]);
682 }
683 }
684 }
685 break;
686 case 'custom':
687 $customOptions = $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'];
688 if (is_array($customOptions)) {
689 foreach ($customOptions as $coKey => $coValue) {
690 if (is_array($coValue['items'])) {
691 // Traverse items:
692 foreach ($coValue['items'] as $itemKey => $itemCfg) {
693 foreach ($elements as $eKey => $value) {
694 if (($coKey . ':' . $itemKey) === (string)$value) {
695 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($itemCfg[0]));
696 }
697 }
698 }
699 }
700 }
701 }
702 break;
703 case 'modListGroup':
704
705 case 'modListUser':
706 if (!$this->loadModules) {
707 $this->loadModules = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Module\\ModuleLoader');
708 $this->loadModules->load($GLOBALS['TBE_MODULES']);
709 }
710 $modList = $specialKey == 'modListUser' ? $this->loadModules->modListUser : $this->loadModules->modListGroup;
711 foreach ($modList as $theModName) {
712 foreach ($elements as $eKey => $value) {
713 $label = '';
714 // Add label for main module:
715 $pp = explode('_', $value);
716 if (count($pp) > 1) {
717 $label .= $GLOBALS['LANG']->moduleLabels['tabs'][($pp[0] . '_tab')] . '>';
718 }
719 // Add modules own label now:
720 $label .= $GLOBALS['LANG']->moduleLabels['tabs'][$value . '_tab'];
721 if ((string)$theModName === (string)$value) {
722 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($label);
723 }
724 }
725 }
726 break;
727 }
728 return $dataAcc;
729 }
730
731 /**
732 * Adds the foreign record elements to $dataAcc, if any
733 *
734 * @param array $dataAcc Array with numeric keys, containing values for the selector box, prepared for interface. We are going to add elements to this array as needed.
735 * @param array $elements The array of original elements - basically the field value exploded by ",
736 * @param array $fieldConfig Field configuration from TCA
737 * @param string $field The field name
738 * @param array $TSconfig TSconfig for the record
739 * @param array $row The record
740 * @param array $table The current table
741 * @return array Modified $dataAcc array
742 * @access private
743 * @see renderRecord_selectProc()
744 */
745 public function selectAddForeign($dataAcc, $elements, $fieldConfig, $field, $TSconfig, $row, $table) {
746 // Init:
747 $recordList = array();
748 // Foreign_table
749 $subres = BackendUtility::exec_foreign_table_where_query($fieldConfig, $field, $TSconfig);
750 while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres)) {
751 // Resolve move-placeholder, to check the right uid against $dataIds
752 BackendUtility::workspaceOL($fieldConfig['config']['foreign_table'], $subrow);
753 $recordList[$subrow['uid']] = BackendUtility::getRecordTitle($fieldConfig['config']['foreign_table'], $subrow);
754 }
755 $GLOBALS['TYPO3_DB']->sql_free_result($subres);
756 // neg_foreign_table
757 if (is_array($GLOBALS['TCA'][$fieldConfig['config']['neg_foreign_table']])) {
758 $subres = BackendUtility::exec_foreign_table_where_query($fieldConfig, $field, $TSconfig, 'neg_');
759 while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres)) {
760 // Resolve move-placeholder, to check the right uid against $dataIds
761 BackendUtility::workspaceOL($fieldConfig['config']['nes_foreign_table'], $subrow);
762 $recordList[-$subrow['uid']] = BackendUtility::getRecordTitle($fieldConfig['config']['neg_foreign_table'], $subrow);
763 }
764 $GLOBALS['TYPO3_DB']->sql_free_result($subres);
765 }
766 // At this point all records that CAN be selected is found in $recordList
767 // Now, get the data from loadDBgroup based on the input list of values.
768 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
769 if ($fieldConfig['config']['MM']) {
770 $dataAcc = array();
771 }
772 // Reset, if MM (which cannot bear anything but real relations!)
773 // After this we can traverse the loadDBgroup values and match values with the list of possible values in $recordList:
774 foreach ($dataIds as $theId) {
775 if (isset($recordList[$theId])) {
776 $lPrefix = $this->sL($fieldConfig['config'][($theId > 0 ? '' : 'neg_') . 'foreign_table_prefix']);
777 if ($fieldConfig['config']['MM'] || $fieldConfig['config']['foreign_field']) {
778 $dataAcc[] = rawurlencode($theId) . '|' . rawurlencode(GeneralUtility::fixed_lgd_cs(($lPrefix . strip_tags($recordList[$theId])), $GLOBALS['BE_USER']->uc['titleLen']));
779 } else {
780 foreach ($elements as $eKey => $value) {
781 if ((int)$theId === (int)$value) {
782 $dataAcc[$eKey] = rawurlencode($theId) . '|' . rawurlencode(GeneralUtility::fixed_lgd_cs(($lPrefix . strip_tags($recordList[$theId])), $GLOBALS['BE_USER']->uc['titleLen']));
783 }
784 }
785 }
786 }
787 }
788 return $dataAcc;
789 }
790
791 /**
792 * Returning the id-list processed by loadDBgroup for the foreign tables.
793 *
794 * @param array $elements The array of original elements - basically the field value exploded by ",
795 * @param array $fieldConfig Field configuration from TCA
796 * @param array $row The data array, currently. Used to set the "local_uid" for selecting MM relation records.
797 * @param string $table Current table name. passed on to \TYPO3\CMS\Core\Database\RelationHandler
798 * @return array An array with ids of the records from the input elements array.
799 * @access private
800 */
801 public function getDataIdList($elements, $fieldConfig, $row, $table) {
802 // Use given uid (might be the uid of a workspace record)
803 $recordId = $row['uid'];
804 // If not dealing with MM relations, then always(!) use the default live uid
805 if (empty($fieldConfig['config']['MM'])) {
806 $recordId = $this->getLiveDefaultId($table, $row['uid']);
807 }
808 $loadDB = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\RelationHandler');
809 $loadDB->registerNonTableValues = $fieldConfig['config']['allowNonIdValues'] ? 1 : 0;
810 $loadDB->start(implode(',', $elements), $fieldConfig['config']['foreign_table'] . ',' . $fieldConfig['config']['neg_foreign_table'], $fieldConfig['config']['MM'], $recordId, $table, $fieldConfig['config']);
811 $idList = $loadDB->convertPosNeg($loadDB->getValueArray(), $fieldConfig['config']['foreign_table'], $fieldConfig['config']['neg_foreign_table']);
812 return $idList;
813 }
814
815 /**
816 * Processing of selector box items. This includes the automated adding of elements plus user-function processing.
817 *
818 * @param array The elements to process
819 * @param array TCA/columns configuration
820 * @param array TSconfig for the field
821 * @param string The table name
822 * @param array The current row
823 * @param string The field name
824 * @return array The modified input $selItems array
825 * @access private
826 * @see renderRecord_selectProc()
827 */
828 public function procesItemArray($selItems, $config, $fieldTSConfig, $table, $row, $field) {
829 $selItems = $this->addItems($selItems, $fieldTSConfig['addItems.']);
830 if ($config['itemsProcFunc']) {
831 $selItems = $this->procItems($selItems, $fieldTSConfig['itemsProcFunc.'], $config, $table, $row, $field);
832 }
833 return $selItems;
834 }
835
836 /**
837 * Adding items from $iArray to $items array
838 *
839 * @param array $items The array of selector box items to which key(value) / value(label) pairs from $iArray will be added.
840 * @param array $iArray The array of elements to add. The keys will become values. The value will become the label.
841 * @return array The modified input $items array
842 * @access private
843 * @see procesItemArray()
844 */
845 public function addItems($items, $iArray) {
846 if (is_array($iArray)) {
847 foreach ($iArray as $value => $label) {
848 $items[] = array($label, $value);
849 }
850 }
851 return $items;
852 }
853
854 /**
855 * User processing of a selector box array of values.
856 *
857 * @param array $items The array of selector box items
858 * @param array $itemsProcFuncTSconfig TSconfig for the fields itemProcFunc
859 * @param array $config TCA/columns configuration
860 * @param string $table The table name
861 * @param array $row The current row
862 * @param string $field The field name
863 * @return array The modified input $items array
864 * @access private
865 * @see procesItemArray()
866 */
867 public function procItems($items, $itemsProcFuncTSconfig, $config, $table, $row, $field) {
868 $params = array();
869 $params['items'] = &$items;
870 $params['config'] = $config;
871 $params['TSconfig'] = $itemsProcFuncTSconfig;
872 $params['table'] = $table;
873 $params['row'] = $row;
874 $params['field'] = $field;
875 GeneralUtility::callUserFunction($config['itemsProcFunc'], $params, $this);
876 return $items;
877 }
878
879 /***********************************************
880 *
881 * Helper functions
882 *
883 ***********************************************/
884 /**
885 * Sets the lock for a record from table/id, IF $this->lockRecords is set!
886 *
887 * @param string $table The table name
888 * @param int $id The id of the record
889 * @param int $pid The pid of the record
890 * @return void
891 */
892 public function lockRecord($table, $id, $pid = 0) {
893 if ($this->lockRecords) {
894 BackendUtility::lockRecords($table, $id, $pid);
895 }
896 }
897
898 /**
899 * Dummy function, can be used to "register" records. Used by eg. the "show_item" script.
900 *
901 * @param string $table Table name
902 * @param int $id Record id
903 * @param string $field Field name
904 * @param string $content Field content.
905 * @return void
906 * @access private
907 * @see renderRecord()
908 */
909 public function regItem($table, $id, $field, $content) {
910
911 }
912
913 /**
914 * Local wrapper function for LANG->sL (returning language labels)
915 *
916 * @param string Language label key
917 * @return string Localized label value.
918 * @access private
919 */
920 public function sL($in) {
921 return $GLOBALS['LANG']->sL($in);
922 }
923
924 /**
925 * Gets the record uid of the live default record. If already
926 * pointing to the live record, the submitted record uid is returned.
927 *
928 * @param string $tableName
929 * @param int $id
930 * @return int
931 */
932 protected function getLiveDefaultId($tableName, $id) {
933 $liveDefaultId = BackendUtility::getLiveVersionIdOfRecord($tableName, $id);
934 if ($liveDefaultId === NULL) {
935 $liveDefaultId = $id;
936 }
937 return $liveDefaultId;
938 }
939
940 }