[!!!][TASK] Remove static file edit magic
[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 // Register items, mostly for external use (overriding the regItem() function)
272 foreach ($totalRecordContent as $field => $data) {
273 $this->regItem($table, $id, $field, $data);
274 }
275 // Finally, store the result:
276 reset($totalRecordContent);
277 return $totalRecordContent;
278 }
279
280 /**
281 * Function with the switch() construct which triggers functions for processing of the data value depending on the TCA-config field type.
282 *
283 * @param string $data Value to process
284 * @param array $fieldConfig TCA/columns array for field (independant of TCA for flexforms - coming from XML then)
285 * @param array $TSconfig TSconfig (blank for flexforms for now)
286 * @param string $table Table name
287 * @param array $row The row array, always of the real record (also for flexforms)
288 * @param string $field The field
289 * @return string Modified $value
290 */
291 public function renderRecord_SW($data, $fieldConfig, $TSconfig, $table, $row, $field) {
292 switch ((string)$fieldConfig['config']['type']) {
293 case 'group':
294 $data = $this->renderRecord_groupProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
295 break;
296 case 'select':
297 $data = $this->renderRecord_selectProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
298 break;
299 case 'flex':
300 $data = $this->renderRecord_flexProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
301 break;
302 case 'inline':
303 $data = $this->renderRecord_inlineProc($data, $fieldConfig, $TSconfig, $table, $row, $field);
304 break;
305 }
306 return $data;
307 }
308
309 /**
310 * Processing of the data value in case the field type is "group"
311 *
312 * @param string $data The field value
313 * @param array $fieldConfig TCA field config
314 * @param array $TSconfig TCEform TSconfig for the record
315 * @param string $table Table name
316 * @param array $row The row
317 * @param string $field Field name
318 * @return string The processed input field value ($data)
319 * @access private
320 * @see renderRecord()
321 */
322 public function renderRecord_groupProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
323 switch ($fieldConfig['config']['internal_type']) {
324 case 'file_reference':
325 case 'file':
326 // Init array used to accumulate the files:
327 $dataAcc = array();
328 // Now, load the files into the $dataAcc array, whether stored by MM or as a list of filenames:
329 if ($fieldConfig['config']['MM']) {
330 $loadDB = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
331 $loadDB->start('', 'files', $fieldConfig['config']['MM'], $row['uid']);
332 // Setting dummy startup
333 foreach ($loadDB->itemArray as $value) {
334 if ($value['id']) {
335 $dataAcc[] = rawurlencode($value['id']) . '|' . rawurlencode(PathUtility::basename($value['id']));
336 }
337 }
338 } else {
339 $fileList = GeneralUtility::trimExplode(',', $data, TRUE);
340 foreach ($fileList as $value) {
341 if ($value) {
342 $dataAcc[] = rawurlencode($value) . '|' . rawurlencode(PathUtility::basename($value));
343 }
344 }
345 }
346 // Implode the accumulation array to a comma separated string:
347 $data = implode(',', $dataAcc);
348 break;
349 case 'db':
350 $loadDB = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
351 /** @var $loadDB \TYPO3\CMS\Core\Database\RelationHandler */
352 $loadDB->start($data, $fieldConfig['config']['allowed'], $fieldConfig['config']['MM'], $row['uid'], $table, $fieldConfig['config']);
353 $loadDB->getFromDB();
354 $data = $loadDB->readyForInterface();
355 break;
356 }
357 return $data;
358 }
359
360 /**
361 * Processing of the data value in case the field type is "select"
362 *
363 * @param string $data The field value
364 * @param array $fieldConfig TCA field config
365 * @param array $TSconfig TCEform TSconfig for the record
366 * @param string $table Table name
367 * @param array $row The row
368 * @param string $field Field name
369 * @return string The processed input field value ($data)
370 * @access private
371 * @see renderRecord()
372 */
373 public function renderRecord_selectProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
374 // Initialize:
375 // Current data set.
376 $elements = GeneralUtility::trimExplode(',', $data, TRUE);
377 // New data set, ready for interface (list of values, rawurlencoded)
378 $dataAcc = array();
379 // For list selectors (multi-value):
380 if ((int)$fieldConfig['config']['maxitems'] > 1 || $fieldConfig['config']['renderMode'] === 'tree') {
381 // Add regular elements:
382 if (!is_array($fieldConfig['config']['items'])) {
383 $fieldConfig['config']['items'] = array();
384 }
385 $fieldConfig['config']['items'] = $this->procesItemArray($fieldConfig['config']['items'], $fieldConfig['config'], $TSconfig[$field], $table, $row, $field);
386 foreach ($fieldConfig['config']['items'] as $pvpv) {
387 foreach ($elements as $eKey => $value) {
388 if ((string)$value === (string)$pvpv[1]) {
389 $dataAcc[$eKey] = rawurlencode($pvpv[1]) . '|' . rawurlencode($this->sL($pvpv[0]));
390 }
391 }
392 }
393 // Add "special"
394 if ($fieldConfig['config']['special']) {
395 $dataAcc = $this->selectAddSpecial($dataAcc, $elements, $fieldConfig['config']['special']);
396 }
397 // Add "foreign table" stuff:
398 if ($GLOBALS['TCA'][$fieldConfig['config']['foreign_table']]) {
399 $dataAcc = $this->selectAddForeign($dataAcc, $elements, $fieldConfig, $field, $TSconfig, $row, $table);
400 }
401 // Always keep the native order for display in interface:
402 ksort($dataAcc);
403 } else {
404 // Normal, <= 1 -> value without title on it
405 if ($GLOBALS['TCA'][$fieldConfig['config']['foreign_table']]) {
406 // Getting the data
407 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
408 if (!count($dataIds)) {
409 $dataIds = array(0);
410 }
411 $dataAcc[] = $dataIds[0];
412 } else {
413 $dataAcc[] = $elements[0];
414 }
415 }
416 return implode(',', $dataAcc);
417 }
418
419 /**
420 * Processing of the data value in case the field type is "flex"
421 * MUST NOT be called in case of already INSIDE a flexform!
422 *
423 * @param string $data The field value
424 * @param array $fieldConfig CA field config
425 * @param array $TSconfig TCEform TSconfig for the record
426 * @param string $table Table name
427 * @param array $row The row
428 * @param string $field Field name
429 * @return string The processed input field value ($data)
430 * @access private
431 * @see renderRecord()
432 */
433 public function renderRecord_flexProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
434 // Convert the XML data to PHP array:
435 if (!is_array($data)) {
436 $currentValueArray = GeneralUtility::xml2array($data);
437 } else {
438 $currentValueArray = $data;
439 }
440 if (is_array($currentValueArray)) {
441 // Get current value array:
442 $dataStructArray = BackendUtility::getFlexFormDS($fieldConfig['config'], $row, $table, $field);
443 // Manipulate Flexform DS via TSConfig and group access lists
444 if (is_array($dataStructArray)) {
445 $flexFormHelper = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Form\FlexFormsHelper::class);
446 $dataStructArray = $flexFormHelper->modifyFlexFormDS($dataStructArray, $table, $field, $row, $fieldConfig);
447 unset($flexFormHelper);
448 }
449 if (is_array($dataStructArray)) {
450 $currentValueArray['data'] = $this->renderRecord_flexProc_procInData($currentValueArray['data'], $dataStructArray, array($data, $fieldConfig, $TSconfig, $table, $row, $field));
451 $flexObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools::class);
452 $data = $flexObj->flexArray2Xml($currentValueArray, TRUE);
453 }
454 }
455 return $data;
456 }
457
458 /**
459 * Processing of the content in $totalRecordcontent based on settings in the types-configuration
460 *
461 * @param array $totalRecordContent The array of values which has been processed according to their type (eg. "group" or "select")
462 * @param array $types_fieldConfig The "types" configuration for the current display of fields.
463 * @param int $tscPID PAGE TSconfig PID
464 * @param string $table Table name
465 * @param int $pid PID value
466 * @return array The processed version of $totalRecordContent
467 * @access private
468 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
469 */
470 public function renderRecord_typesProc($totalRecordContent, $types_fieldConfig, $tscPID, $table, $pid) {
471 GeneralUtility::logDeprecatedFunction();
472 return $totalRecordContent;
473 }
474
475 /**
476 * Processing of the data value in case the field type is "inline"
477 * In some parts nearly the same as type "select"
478 *
479 * @param string $data The field value
480 * @param array $fieldConfig TCA field config
481 * @param array $TSconfig TCEform TSconfig for the record
482 * @param string $table Table name
483 * @param array $row The row
484 * @param string $field Field name
485 * @return string The processed input field value ($data)
486 * @access private
487 * @see renderRecord()
488 */
489 public function renderRecord_inlineProc($data, $fieldConfig, $TSconfig, $table, $row, $field) {
490 // Initialize:
491 // Current data set.
492 $elements = GeneralUtility::trimExplode(',', $data);
493 // New data set, ready for interface (list of values, rawurlencoded)
494 $dataAcc = array();
495 // At this point all records that CAN be selected is found in $recordList
496 // Now, get the data from loadDBgroup based on the input list of values.
497 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
498 // After this we can traverse the loadDBgroup values and match values with the list of possible values in $recordList:
499 foreach ($dataIds as $theId) {
500 if ($fieldConfig['config']['MM'] || $fieldConfig['config']['foreign_field']) {
501 $dataAcc[] = $theId;
502 } else {
503 foreach ($elements as $eKey => $value) {
504 if ((int)$theId === (int)$value) {
505 $dataAcc[$eKey] = $theId;
506 }
507 }
508 }
509 }
510 return implode(',', $dataAcc);
511 }
512
513 /***********************************************
514 *
515 * FlexForm processing functions
516 *
517 ***********************************************/
518 /**
519 * Function traversing sheets/languages for flex form data structures
520 *
521 * @param array $dataPart Data array
522 * @param array $dataStructArray Data Structure array
523 * @param array $pParams Various parameters to pass-through
524 * @return array Modified $dataPart array.
525 * @access private
526 * @see \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_flex_procInData(), renderRecord_flexProc_procInData_travDS()
527 */
528 public function renderRecord_flexProc_procInData($dataPart, $dataStructArray, $pParams) {
529 if (is_array($dataPart)) {
530 foreach ($dataPart as $sKey => $sheetDef) {
531 list($dataStruct, $actualSheet) = GeneralUtility::resolveSheetDefInDS($dataStructArray, $sKey);
532 if (is_array($dataStruct) && $actualSheet == $sKey && is_array($sheetDef)) {
533 foreach ($sheetDef as $lKey => $lData) {
534 $this->renderRecord_flexProc_procInData_travDS($dataPart[$sKey][$lKey], $dataStruct['ROOT']['el'], $pParams);
535 }
536 }
537 }
538 }
539 return $dataPart;
540 }
541
542 /**
543 * Traverse data array / structure
544 *
545 * @param array $dataValues Data array passed by reference.
546 * @param array $DSelements Data structure
547 * @param array $pParams Various parameters pass-through.
548 * @return void
549 * @see \TYPO3\CMS\Core\DataHandling\DataHandler::checkValue_flex_procInData(), renderRecord_flexProc_procInData_travDS()
550 */
551 public function renderRecord_flexProc_procInData_travDS(&$dataValues, $DSelements, $pParams) {
552 if (is_array($DSelements)) {
553 // For each DS element:
554 foreach ($DSelements as $key => $dsConf) {
555 // Array/Section:
556 if ($DSelements[$key]['type'] == 'array') {
557 if (is_array($dataValues[$key]['el'])) {
558 if ($DSelements[$key]['section']) {
559 foreach ($dataValues[$key]['el'] as $ik => $el) {
560 if (is_array($el)) {
561 $theKey = key($el);
562 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) {
563 $this->renderRecord_flexProc_procInData_travDS($dataValues[$key]['el'][$ik][$theKey]['el'], $DSelements[$key]['el'][$theKey]['el'], $pParams);
564 }
565 }
566 }
567 } else {
568 if (!isset($dataValues[$key]['el'])) {
569 $dataValues[$key]['el'] = array();
570 }
571 $this->renderRecord_flexProc_procInData_travDS($dataValues[$key]['el'], $DSelements[$key]['el'], $pParams);
572 }
573 }
574 } else {
575 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) {
576 foreach ($dataValues[$key] as $vKey => $data) {
577 // $data,$fieldConfig,$TSconfig,$table,$row,$field
578 list(, , $CVTSconfig, $CVtable, $CVrow, $CVfield) = $pParams;
579 // Set default value:
580 if (!isset($dataValues[$key][$vKey])) {
581 $dataValues[$key][$vKey] = $dsConf['TCEforms']['config']['default'];
582 }
583 // Process value:
584 $dataValues[$key][$vKey] = $this->renderRecord_SW($dataValues[$key][$vKey], $dsConf['TCEforms'], $CVTSconfig, $CVtable, $CVrow, $CVfield);
585 }
586 }
587 }
588 }
589 }
590 }
591
592 /***********************************************
593 *
594 * Selector box processing functions
595 *
596 ***********************************************/
597 /**
598 * Adding "special" types to the $dataAcc array of selector items
599 *
600 * @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.
601 * @param array $elements The array of original elements - basically the field value exploded by ",
602 * @param string $specialKey The "special" key from the TCA config of the field. Determines the type of processing in here.
603 * @return array Modified $dataAcc array
604 * @access private
605 * @see renderRecord_selectProc()
606 */
607 public function selectAddSpecial($dataAcc, $elements, $specialKey) {
608 // Special select types:
609 switch ((string)$specialKey) {
610 case 'tables':
611 $tNames = array_keys($GLOBALS['TCA']);
612 foreach ($tNames as $tableName) {
613 foreach ($elements as $eKey => $value) {
614 if ((string)$tableName === (string)$value) {
615 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($GLOBALS['TCA'][$value]['ctrl']['title']));
616 }
617 }
618 }
619 break;
620 case 'pagetypes':
621 $theTypes = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
622 if (is_array($theTypes)) {
623 foreach ($theTypes as $theTypesArrays) {
624 foreach ($elements as $eKey => $value) {
625 if ((string)$theTypesArrays[1] === (string)$value) {
626 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($theTypesArrays[0]));
627 }
628 }
629 }
630 }
631 break;
632 case 'exclude':
633 $theExcludeFields = BackendUtility::getExcludeFields();
634 if (is_array($theExcludeFields)) {
635 foreach ($theExcludeFields as $theExcludeFieldsArrays) {
636 foreach ($elements as $eKey => $value) {
637 if ((string)$theExcludeFieldsArrays[1] === (string)$value) {
638 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode(rtrim($theExcludeFieldsArrays[0], ':'));
639 }
640 }
641 }
642 }
643 break;
644 case 'explicitValues':
645 $theTypes = BackendUtility::getExplicitAuthFieldValues();
646 foreach ($theTypes as $tableFieldKey => $theTypeArrays) {
647 if (is_array($theTypeArrays['items'])) {
648 foreach ($theTypeArrays['items'] as $itemValue => $itemContent) {
649 foreach ($elements as $eKey => $value) {
650 if (($tableFieldKey . ':' . $itemValue . ':' . $itemContent[0]) === (string)$value) {
651 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode(('[' . $itemContent[2] . '] ' . $itemContent[1]));
652 }
653 }
654 }
655 }
656 }
657 break;
658 case 'languages':
659 $theLangs = BackendUtility::getSystemLanguages();
660 foreach ($theLangs as $lCfg) {
661 foreach ($elements as $eKey => $value) {
662 if ((string)$lCfg[1] === (string)$value) {
663 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($lCfg[0]);
664 }
665 }
666 }
667 break;
668 case 'custom':
669 $customOptions = $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'];
670 if (is_array($customOptions)) {
671 foreach ($customOptions as $coKey => $coValue) {
672 if (is_array($coValue['items'])) {
673 // Traverse items:
674 foreach ($coValue['items'] as $itemKey => $itemCfg) {
675 foreach ($elements as $eKey => $value) {
676 if (($coKey . ':' . $itemKey) === (string)$value) {
677 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($this->sL($itemCfg[0]));
678 }
679 }
680 }
681 }
682 }
683 }
684 break;
685 case 'modListGroup':
686
687 case 'modListUser':
688 if (!$this->loadModules) {
689 $this->loadModules = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Module\ModuleLoader::class);
690 $this->loadModules->load($GLOBALS['TBE_MODULES']);
691 }
692 $modList = $specialKey == 'modListUser' ? $this->loadModules->modListUser : $this->loadModules->modListGroup;
693 foreach ($modList as $theModName) {
694 foreach ($elements as $eKey => $value) {
695 $label = '';
696 // Add label for main module:
697 $pp = explode('_', $value);
698 if (count($pp) > 1) {
699 $label .= $GLOBALS['LANG']->moduleLabels['tabs'][($pp[0] . '_tab')] . '>';
700 }
701 // Add modules own label now:
702 $label .= $GLOBALS['LANG']->moduleLabels['tabs'][$value . '_tab'];
703 if ((string)$theModName === (string)$value) {
704 $dataAcc[$eKey] = rawurlencode($value) . '|' . rawurlencode($label);
705 }
706 }
707 }
708 break;
709 }
710 return $dataAcc;
711 }
712
713 /**
714 * Adds the foreign record elements to $dataAcc, if any
715 *
716 * @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.
717 * @param array $elements The array of original elements - basically the field value exploded by ",
718 * @param array $fieldConfig Field configuration from TCA
719 * @param string $field The field name
720 * @param array $TSconfig TSconfig for the record
721 * @param array $row The record
722 * @param array $table The current table
723 * @return array Modified $dataAcc array
724 * @access private
725 * @see renderRecord_selectProc()
726 */
727 public function selectAddForeign($dataAcc, $elements, $fieldConfig, $field, $TSconfig, $row, $table) {
728 // Init:
729 $recordList = array();
730 // Foreign_table
731 $subres = BackendUtility::exec_foreign_table_where_query($fieldConfig, $field, $TSconfig);
732 while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres)) {
733 // Resolve move-placeholder, to check the right uid against $dataIds
734 BackendUtility::workspaceOL($fieldConfig['config']['foreign_table'], $subrow);
735 $recordList[$subrow['uid']] = BackendUtility::getRecordTitle($fieldConfig['config']['foreign_table'], $subrow);
736 }
737 $GLOBALS['TYPO3_DB']->sql_free_result($subres);
738 // neg_foreign_table
739 if (is_array($GLOBALS['TCA'][$fieldConfig['config']['neg_foreign_table']])) {
740 $subres = BackendUtility::exec_foreign_table_where_query($fieldConfig, $field, $TSconfig, 'neg_');
741 while ($subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres)) {
742 // Resolve move-placeholder, to check the right uid against $dataIds
743 BackendUtility::workspaceOL($fieldConfig['config']['nes_foreign_table'], $subrow);
744 $recordList[-$subrow['uid']] = BackendUtility::getRecordTitle($fieldConfig['config']['neg_foreign_table'], $subrow);
745 }
746 $GLOBALS['TYPO3_DB']->sql_free_result($subres);
747 }
748 // At this point all records that CAN be selected is found in $recordList
749 // Now, get the data from loadDBgroup based on the input list of values.
750 $dataIds = $this->getDataIdList($elements, $fieldConfig, $row, $table);
751 if ($fieldConfig['config']['MM']) {
752 $dataAcc = array();
753 }
754 // Reset, if MM (which cannot bear anything but real relations!)
755 // After this we can traverse the loadDBgroup values and match values with the list of possible values in $recordList:
756 foreach ($dataIds as $theId) {
757 if (isset($recordList[$theId])) {
758 $lPrefix = $this->sL($fieldConfig['config'][($theId > 0 ? '' : 'neg_') . 'foreign_table_prefix']);
759 if ($fieldConfig['config']['MM'] || $fieldConfig['config']['foreign_field']) {
760 $dataAcc[] = rawurlencode($theId) . '|' . rawurlencode(GeneralUtility::fixed_lgd_cs(($lPrefix . strip_tags($recordList[$theId])), $GLOBALS['BE_USER']->uc['titleLen']));
761 } else {
762 foreach ($elements as $eKey => $value) {
763 if ((int)$theId === (int)$value) {
764 $dataAcc[$eKey] = rawurlencode($theId) . '|' . rawurlencode(GeneralUtility::fixed_lgd_cs(($lPrefix . strip_tags($recordList[$theId])), $GLOBALS['BE_USER']->uc['titleLen']));
765 }
766 }
767 }
768 }
769 }
770 return $dataAcc;
771 }
772
773 /**
774 * Returning the id-list processed by loadDBgroup for the foreign tables.
775 *
776 * @param array $elements The array of original elements - basically the field value exploded by ",
777 * @param array $fieldConfig Field configuration from TCA
778 * @param array $row The data array, currently. Used to set the "local_uid" for selecting MM relation records.
779 * @param string $table Current table name. passed on to \TYPO3\CMS\Core\Database\RelationHandler
780 * @return array An array with ids of the records from the input elements array.
781 * @access private
782 */
783 public function getDataIdList($elements, $fieldConfig, $row, $table) {
784 // Use given uid (might be the uid of a workspace record)
785 $recordId = $row['uid'];
786 // If not dealing with MM relations, then always(!) use the default live uid
787 if (empty($fieldConfig['config']['MM'])) {
788 $recordId = $this->getLiveDefaultId($table, $row['uid']);
789 }
790 $loadDB = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
791 $loadDB->registerNonTableValues = $fieldConfig['config']['allowNonIdValues'] ? 1 : 0;
792 $loadDB->start(implode(',', $elements), $fieldConfig['config']['foreign_table'] . ',' . $fieldConfig['config']['neg_foreign_table'], $fieldConfig['config']['MM'], $recordId, $table, $fieldConfig['config']);
793 $idList = $loadDB->convertPosNeg($loadDB->getValueArray(), $fieldConfig['config']['foreign_table'], $fieldConfig['config']['neg_foreign_table']);
794 return $idList;
795 }
796
797 /**
798 * Processing of selector box items. This includes the automated adding of elements plus user-function processing.
799 *
800 * @param array The elements to process
801 * @param array TCA/columns configuration
802 * @param array TSconfig for the field
803 * @param string The table name
804 * @param array The current row
805 * @param string The field name
806 * @return array The modified input $selItems array
807 * @access private
808 * @see renderRecord_selectProc()
809 */
810 public function procesItemArray($selItems, $config, $fieldTSConfig, $table, $row, $field) {
811 $selItems = $this->addItems($selItems, $fieldTSConfig['addItems.']);
812 if ($config['itemsProcFunc']) {
813 $selItems = $this->procItems($selItems, $fieldTSConfig['itemsProcFunc.'], $config, $table, $row, $field);
814 }
815 return $selItems;
816 }
817
818 /**
819 * Adding items from $iArray to $items array
820 *
821 * @param array $items The array of selector box items to which key(value) / value(label) pairs from $iArray will be added.
822 * @param array $iArray The array of elements to add. The keys will become values. The value will become the label.
823 * @return array The modified input $items array
824 * @access private
825 * @see procesItemArray()
826 */
827 public function addItems($items, $iArray) {
828 if (is_array($iArray)) {
829 foreach ($iArray as $value => $label) {
830 $items[] = array($label, $value);
831 }
832 }
833 return $items;
834 }
835
836 /**
837 * User processing of a selector box array of values.
838 *
839 * @param array $items The array of selector box items
840 * @param array $itemsProcFuncTSconfig TSconfig for the fields itemProcFunc
841 * @param array $config TCA/columns configuration
842 * @param string $table The table name
843 * @param array $row The current row
844 * @param string $field The field name
845 * @return array The modified input $items array
846 * @access private
847 * @see procesItemArray()
848 */
849 public function procItems($items, $itemsProcFuncTSconfig, $config, $table, $row, $field) {
850 $params = array();
851 $params['items'] = &$items;
852 $params['config'] = $config;
853 $params['TSconfig'] = $itemsProcFuncTSconfig;
854 $params['table'] = $table;
855 $params['row'] = $row;
856 $params['field'] = $field;
857 GeneralUtility::callUserFunction($config['itemsProcFunc'], $params, $this);
858 return $items;
859 }
860
861 /***********************************************
862 *
863 * Helper functions
864 *
865 ***********************************************/
866 /**
867 * Sets the lock for a record from table/id, IF $this->lockRecords is set!
868 *
869 * @param string $table The table name
870 * @param int $id The id of the record
871 * @param int $pid The pid of the record
872 * @return void
873 */
874 public function lockRecord($table, $id, $pid = 0) {
875 if ($this->lockRecords) {
876 BackendUtility::lockRecords($table, $id, $pid);
877 }
878 }
879
880 /**
881 * Dummy function, can be used to "register" records. Used by eg. the "show_item" script.
882 *
883 * @param string $table Table name
884 * @param int $id Record id
885 * @param string $field Field name
886 * @param string $content Field content.
887 * @return void
888 * @access private
889 * @see renderRecord()
890 */
891 public function regItem($table, $id, $field, $content) {
892
893 }
894
895 /**
896 * Local wrapper function for LANG->sL (returning language labels)
897 *
898 * @param string Language label key
899 * @return string Localized label value.
900 * @access private
901 */
902 public function sL($in) {
903 return $GLOBALS['LANG']->sL($in);
904 }
905
906 /**
907 * Gets the record uid of the live default record. If already
908 * pointing to the live record, the submitted record uid is returned.
909 *
910 * @param string $tableName
911 * @param int $id
912 * @return int
913 */
914 protected function getLiveDefaultId($tableName, $id) {
915 $liveDefaultId = BackendUtility::getLiveVersionIdOfRecord($tableName, $id);
916 if ($liveDefaultId === NULL) {
917 $liveDefaultId = $id;
918 }
919 return $liveDefaultId;
920 }
921
922 }