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