d7c1b64ad7f88881b78d5969e396d96961907ef3
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / AbstractItemProvider.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\FormDataProvider;
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\Module\ModuleLoader;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Database\DatabaseConnection;
21 use TYPO3\CMS\Core\Database\RelationHandler;
22 use TYPO3\CMS\Core\Imaging\IconFactory;
23 use TYPO3\CMS\Core\Messaging\FlashMessage;
24 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
25 use TYPO3\CMS\Core\Messaging\FlashMessageService;
26 use TYPO3\CMS\Core\Utility\ArrayUtility;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28 use TYPO3\CMS\Core\Utility\MathUtility;
29 use TYPO3\CMS\Core\Utility\PathUtility;
30 use TYPO3\CMS\Lang\LanguageService;
31
32 /**
33 * Contains methods used by Data providers that handle elements
34 * with single items like select, radio and some more.
35 */
36 abstract class AbstractItemProvider
37 {
38 /**
39 * Resolve "itemProcFunc" of elements.
40 *
41 * @param array $result Main result array
42 * @param string $fieldName Field name to handle item list for
43 * @param array $items Existing items array
44 * @return array New list of item elements
45 */
46 protected function resolveItemProcessorFunction(array $result, $fieldName, array $items)
47 {
48 $table = $result['tableName'];
49 $config = $result['processedTca']['columns'][$fieldName]['config'];
50
51 $pageTsProcessorParameters = null;
52 if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'])) {
53 $pageTsProcessorParameters = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'];
54 }
55 $processorParameters = [
56 // Function manipulates $items directly and return nothing
57 'items' => &$items,
58 'config' => $config,
59 'TSconfig' => $pageTsProcessorParameters,
60 'table' => $table,
61 'row' => $result['databaseRow'],
62 'field' => $fieldName,
63 ];
64 if (!empty($result['flexParentDatabaseRow'])) {
65 $processorParameters['flexParentDatabaseRow'] = $result['flexParentDatabaseRow'];
66 }
67
68 try {
69 GeneralUtility::callUserFunction($config['itemsProcFunc'], $processorParameters, $this);
70 } catch (\Exception $exception) {
71 // The itemsProcFunc method may throw an exception, create a flash message if so
72 $languageService = $this->getLanguageService();
73 $fieldLabel = $fieldName;
74 if (!empty($result['processedTca']['columns'][$fieldName]['label'])) {
75 $fieldLabel = $languageService->sL($result['processedTca']['columns'][$fieldName]['label']);
76 }
77 $message = sprintf(
78 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.items_proc_func_error'),
79 $fieldLabel,
80 $exception->getMessage()
81 );
82 /** @var FlashMessage $flashMessage */
83 $flashMessage = GeneralUtility::makeInstance(
84 FlashMessage::class,
85 $message,
86 '',
87 FlashMessage::ERROR,
88 true
89 );
90 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
91 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
92 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
93 $defaultFlashMessageQueue->enqueue($flashMessage);
94 }
95
96 return $items;
97 }
98
99 /**
100 * PageTsConfig addItems:
101 *
102 * TCEFORMS.aTable.aField[.types][.aType].addItems.aValue = aLabel,
103 * with type specific options merged by pageTsConfig already
104 *
105 * Used by TcaSelectItems and TcaSelectTreeItems data providers
106 *
107 * @param array $result result array
108 * @param string $fieldName Current handle field name
109 * @param array $items Incoming items
110 * @return array Modified item array
111 */
112 protected function addItemsFromPageTsConfig(array $result, $fieldName, array $items)
113 {
114 $table = $result['tableName'];
115 if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
116 && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
117 ) {
118 $addItemsArray = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'];
119 foreach ($addItemsArray as $value => $label) {
120 // If the value ends with a dot, it is a subelement like "34.icon = mylabel.png", skip it
121 if (substr($value, -1) === '.') {
122 continue;
123 }
124 // Check if value "34 = mylabel" also has a "34.icon = myImage.png"
125 $icon = null;
126 if (isset($addItemsArray[$value . '.'])
127 && is_array($addItemsArray[$value . '.'])
128 && !empty($addItemsArray[$value . '.']['icon'])
129 ) {
130 $icon = $addItemsArray[$value . '.']['icon'];
131 }
132 $items[] = array($label, $value, $icon);
133 }
134 }
135 return $items;
136 }
137
138 /**
139 * TCA config "special" evaluation. Add them to $items
140 *
141 * Used by TcaSelectItems and TcaSelectTreeItems data providers
142 *
143 * @param array $result Result array
144 * @param string $fieldName Current handle field name
145 * @param array $items Incoming items
146 * @return array Modified item array
147 * @throws \UnexpectedValueException
148 */
149 protected function addItemsFromSpecial(array $result, $fieldName, array $items)
150 {
151 // Guard
152 if (empty($result['processedTca']['columns'][$fieldName]['config']['special'])
153 || !is_string($result['processedTca']['columns'][$fieldName]['config']['special'])
154 ) {
155 return $items;
156 }
157
158 $languageService = $this->getLanguageService();
159 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
160
161 $special = $result['processedTca']['columns'][$fieldName]['config']['special'];
162 switch (true) {
163 case ($special === 'tables'):
164 foreach ($GLOBALS['TCA'] as $currentTable => $_) {
165 if (!empty($GLOBALS['TCA'][$currentTable]['ctrl']['adminOnly'])) {
166 // Hide "admin only" tables
167 continue;
168 }
169 $label = !empty($GLOBALS['TCA'][$currentTable]['ctrl']['title']) ? $GLOBALS['TCA'][$currentTable]['ctrl']['title'] : '';
170 $icon = $iconFactory->mapRecordTypeToIconIdentifier($currentTable, []);
171 $helpText = [];
172 $languageService->loadSingleTableDescription($currentTable);
173 // @todo: check if this actually works, currently help texts are missing
174 $helpTextArray = $GLOBALS['TCA_DESCR'][$currentTable]['columns'][''];
175 if (!empty($helpTextArray['description'])) {
176 $helpText['description'] = $helpTextArray['description'];
177 }
178 $items[] = [$label, $currentTable, $icon, $helpText];
179 }
180 break;
181 case ($special === 'pagetypes'):
182 if (isset($GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'])
183 && is_array($GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'])
184 ) {
185 $specialItems = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
186 foreach ($specialItems as $specialItem) {
187 if (!is_array($specialItem) || $specialItem[1] === '--div--') {
188 // Skip non arrays and divider items
189 continue;
190 }
191 $label = $specialItem[0];
192 $value = $specialItem[1];
193 $icon = $iconFactory->mapRecordTypeToIconIdentifier('pages', ['doktype' => $specialItem[1]]);
194 $items[] = [$label, $value, $icon];
195 }
196 }
197 break;
198 case ($special === 'exclude'):
199 $excludeArrays = $this->getExcludeFields();
200 foreach ($excludeArrays as $excludeArray) {
201 // If the field comes from a FlexForm, the syntax is more complex
202 if ($excludeArray['origin'] === 'flexForm') {
203 // The field comes from a plugins FlexForm
204 // Add header if not yet set for plugin section
205 if (!isset($items[$excludeArray['sectionHeader']])) {
206 // there is no icon handling for plugins - we take the icon from the table
207 $icon = $iconFactory->mapRecordTypeToIconIdentifier($excludeArray['table'], array());
208 $items[$excludeArray['sectionHeader']] = [
209 $excludeArray['sectionHeader'],
210 '--div--',
211 $icon
212 ];
213 }
214 } else {
215 // Add header if not yet set for table
216 if (!isset($items[$excludeArray['table']])) {
217 $icon = $iconFactory->mapRecordTypeToIconIdentifier($excludeArray['table'], array());
218 $items[$excludeArray['table']] = [
219 $GLOBALS['TCA'][$excludeArray['table']]['ctrl']['title'],
220 '--div--',
221 $icon
222 ];
223 }
224 }
225 // Add help text
226 $helpText = [];
227 $languageService->loadSingleTableDescription($excludeArray['table']);
228 $helpTextArray = $GLOBALS['TCA_DESCR'][$excludeArray['table']]['columns'][$excludeArray['table']];
229 if (!empty($helpTextArray['description'])) {
230 $helpText['description'] = $helpTextArray['description'];
231 }
232 // Item configuration:
233 $items[] = array(
234 rtrim($excludeArray['origin'] === 'flexForm' ? $excludeArray['fieldLabel'] : $languageService->sL($GLOBALS['TCA'][$excludeArray['table']]['columns'][$excludeArray['fieldName']]['label']), ':') . ' (' . $excludeArray['fieldName'] . ')',
235 $excludeArray['table'] . ':' . $excludeArray['fullField'] ,
236 'empty-empty',
237 $helpText
238 );
239 }
240 break;
241 case ($special === 'explicitValues'):
242 $theTypes = $this->getExplicitAuthFieldValues();
243 $icons = [
244 'ALLOW' => 'status-status-permission-granted',
245 'DENY' => 'status-status-permission-denied'
246 ];
247 // Traverse types:
248 foreach ($theTypes as $tableFieldKey => $theTypeArrays) {
249 if (is_array($theTypeArrays['items'])) {
250 // Add header:
251 $items[] = [
252 $theTypeArrays['tableFieldLabel'],
253 '--div--',
254 ];
255 // Traverse options for this field:
256 foreach ($theTypeArrays['items'] as $itemValue => $itemContent) {
257 // Add item to be selected:
258 $items[] = [
259 '[' . $itemContent[2] . '] ' . $itemContent[1],
260 $tableFieldKey . ':' . preg_replace('/[:|,]/', '', $itemValue) . ':' . $itemContent[0],
261 $icons[$itemContent[0]]
262 ];
263 }
264 }
265 }
266 break;
267 case ($special === 'languages'):
268 foreach ($result['systemLanguageRows'] as $language) {
269 if ($language['uid'] !== -1) {
270 $items[] = [
271 0 => $language['title'] . ' [' . $language['uid'] . ']',
272 1 => $language['uid'],
273 2 => $language['flagIconIdentifier']
274 ];
275 }
276 }
277 break;
278 case ($special === 'custom'):
279 $customOptions = $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'];
280 if (is_array($customOptions)) {
281 foreach ($customOptions as $coKey => $coValue) {
282 if (is_array($coValue['items'])) {
283 // Add header:
284 $items[] = [
285 $languageService->sL($coValue['header']),
286 '--div--'
287 ];
288 // Traverse items:
289 foreach ($coValue['items'] as $itemKey => $itemCfg) {
290 $icon = 'empty-empty';
291 $helpText = [];
292 if (!empty($itemCfg[2])) {
293 $helpText['description'] = $languageService->sL($itemCfg[2]);
294 }
295 $items[] = [
296 $languageService->sL($itemCfg[0]),
297 $coKey . ':' . preg_replace('/[:|,]/', '', $itemKey),
298 $icon,
299 $helpText
300 ];
301 }
302 }
303 }
304 }
305 break;
306 case ($special === 'modListGroup' || $special === 'modListUser'):
307 $loadModules = GeneralUtility::makeInstance(ModuleLoader::class);
308 $loadModules->load($GLOBALS['TBE_MODULES']);
309 $modList = $special === 'modListUser' ? $loadModules->modListUser : $loadModules->modListGroup;
310 if (is_array($modList)) {
311 foreach ($modList as $theMod) {
312 // Icon:
313 $icon = $languageService->moduleLabels['tabs_images'][$theMod . '_tab'];
314 if ($icon) {
315 $icon = '../' . PathUtility::stripPathSitePrefix($icon);
316 }
317 // Add help text
318 $helpText = [
319 'title' => $languageService->moduleLabels['labels'][$theMod . '_tablabel'],
320 'description' => $languageService->moduleLabels['labels'][$theMod . '_tabdescr']
321 ];
322
323 $label = '';
324 // Add label for main module:
325 $pp = explode('_', $theMod);
326 if (count($pp) > 1) {
327 $label .= $languageService->moduleLabels['tabs'][$pp[0] . '_tab'] . '>';
328 }
329 // Add modules own label now:
330 $label .= $languageService->moduleLabels['tabs'][$theMod . '_tab'];
331
332 // Item configuration:
333 $items[] = [$label, $theMod, $icon, $helpText];
334 }
335 }
336 break;
337 default:
338 throw new \UnexpectedValueException(
339 'Unknown special value ' . $special . ' for field ' . $fieldName . ' of table ' . $result['tableName'],
340 1439298496
341 );
342 }
343 return $items;
344 }
345
346 /**
347 * TCA config "fileFolder" evaluation. Add them to $items
348 *
349 * Used by TcaSelectItems and TcaSelectTreeItems data providers
350 *
351 * @param array $result Result array
352 * @param string $fieldName Current handle field name
353 * @param array $items Incoming items
354 * @return array Modified item array
355 */
356 protected function addItemsFromFolder(array $result, $fieldName, array $items)
357 {
358 if (empty($result['processedTca']['columns'][$fieldName]['config']['fileFolder'])
359 || !is_string($result['processedTca']['columns'][$fieldName]['config']['fileFolder'])
360 ) {
361 return $items;
362 }
363
364 $fileFolder = $result['processedTca']['columns'][$fieldName]['config']['fileFolder'];
365 $fileFolder = GeneralUtility::getFileAbsFileName($fileFolder);
366 $fileFolder = rtrim($fileFolder, '/') . '/';
367
368 if (@is_dir($fileFolder)) {
369 $fileExtensionList = '';
370 if (!empty($result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'])
371 && is_string($result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'])
372 ) {
373 $fileExtensionList = $result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'];
374 }
375 $recursionLevels = isset($fieldValue['config']['fileFolder_recursions'])
376 ? MathUtility::forceIntegerInRange($fieldValue['config']['fileFolder_recursions'], 0, 99)
377 : 99;
378 $fileArray = GeneralUtility::getAllFilesAndFoldersInPath([], $fileFolder, $fileExtensionList, 0, $recursionLevels);
379 $fileArray = GeneralUtility::removePrefixPathFromList($fileArray, $fileFolder);
380 foreach ($fileArray as $fileReference) {
381 $fileInformation = pathinfo($fileReference);
382 $icon = GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($fileInformation['extension']))
383 ? '../' . PathUtility::stripPathSitePrefix($fileFolder) . $fileReference
384 : '';
385 $items[] = [
386 $fileReference,
387 $fileReference,
388 $icon
389 ];
390 }
391 }
392
393 return $items;
394 }
395
396 /**
397 * TCA config "foreign_table" evaluation. Add them to $items
398 *
399 * Used by TcaSelectItems and TcaSelectTreeItems data providers
400 *
401 * @param array $result Result array
402 * @param string $fieldName Current handle field name
403 * @param array $items Incoming items
404 * @return array Modified item array
405 * @throws \UnexpectedValueException
406 */
407 protected function addItemsFromForeignTable(array $result, $fieldName, array $items)
408 {
409 // Guard
410 if (empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
411 || !is_string($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
412 ) {
413 return $items;
414 }
415
416 $languageService = $this->getLanguageService();
417 $database = $this->getDatabaseConnection();
418
419 $foreignTable = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
420
421 if (!is_array($GLOBALS['TCA'][$foreignTable])) {
422 throw new \UnexpectedValueException(
423 'Field ' . $fieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
424 . $foreignTable . ', but this table is not defined in TCA',
425 1439569743
426 );
427 }
428
429 $foreignTableQueryArray = $this->buildForeignTableQuery($result, $fieldName);
430 $queryResource = $database->exec_SELECT_queryArray($foreignTableQueryArray);
431
432 // Early return on error with flash message
433 $databaseError = $database->sql_error();
434 if (!empty($databaseError)) {
435 $msg = $databaseError . '. ';
436 $msg .= $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch');
437 $msgTitle = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch_title');
438 /** @var $flashMessage FlashMessage */
439 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $msg, $msgTitle, FlashMessage::ERROR, true);
440 /** @var $flashMessageService FlashMessageService */
441 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
442 /** @var $defaultFlashMessageQueue FlashMessageQueue */
443 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
444 $defaultFlashMessageQueue->enqueue($flashMessage);
445 $database->sql_free_result($queryResource);
446 return $items;
447 }
448
449 $labelPrefix = '';
450 if (!empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'])) {
451 $labelPrefix = $result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'];
452 $labelPrefix = $languageService->sL($labelPrefix);
453 }
454
455 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
456
457 while ($foreignRow = $database->sql_fetch_assoc($queryResource)) {
458 BackendUtility::workspaceOL($foreignTable, $foreignRow);
459 if (is_array($foreignRow)) {
460 // If the foreign table sets selicon_field, this field can contain an image
461 // that represents this specific row.
462 $iconFieldName = '';
463 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'])) {
464 $iconFieldName = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'];
465 }
466 $iconPath = '';
467 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'])) {
468 $iconPath = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'];
469 }
470 if ($iconFieldName && $iconPath && $foreignRow[$iconFieldName]) {
471 // Prepare the row icon if available
472 $iParts = GeneralUtility::trimExplode(',', $foreignRow[$iconFieldName], true);
473 $icon = '../' . $iconPath . '/' . trim($iParts[0]);
474 } else {
475 // Else, determine icon based on record type, or a generic fallback
476 $icon = $iconFactory->mapRecordTypeToIconIdentifier($foreignTable, $foreignRow);
477 }
478 // Add the item
479 $items[] = [
480 $labelPrefix . htmlspecialchars(BackendUtility::getRecordTitle($foreignTable, $foreignRow)),
481 $foreignRow['uid'],
482 $icon
483 ];
484 }
485 }
486
487 $database->sql_free_result($queryResource);
488
489 return $items;
490 }
491
492 /**
493 * Remove items using "keepItems" pageTsConfig
494 *
495 * Used by TcaSelectItems and TcaSelectTreeItems data providers
496 *
497 * @param array $result Result array
498 * @param string $fieldName Current handle field name
499 * @param array $items Incoming items
500 * @return array Modified item array
501 */
502 protected function removeItemsByKeepItemsPageTsConfig(array $result, $fieldName, array $items)
503 {
504 $table = $result['tableName'];
505 if (!isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
506 || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
507 ) {
508 return $items;
509 }
510
511 // If keepItems is set but is an empty list all current items get removed
512 if (empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
513 && $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'] !== '0') {
514 return [];
515 }
516
517 return ArrayUtility::keepItemsInArray(
518 $items,
519 $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'],
520 function ($value) {
521 return $value[1];
522 }
523 );
524 }
525
526 /**
527 * Remove items using "removeItems" pageTsConfig
528 *
529 * Used by TcaSelectItems and TcaSelectTreeItems data providers
530 *
531 * @param array $result Result array
532 * @param string $fieldName Current handle field name
533 * @param array $items Incoming items
534 * @return array Modified item array
535 */
536 protected function removeItemsByRemoveItemsPageTsConfig(array $result, $fieldName, array $items)
537 {
538 $table = $result['tableName'];
539 if (empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
540 || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
541 ) {
542 return $items;
543 }
544
545 $removeItems = GeneralUtility::trimExplode(
546 ',',
547 $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'],
548 true
549 );
550 foreach ($items as $key => $itemValues) {
551 if (in_array($itemValues[1], $removeItems)) {
552 unset($items[$key]);
553 }
554 }
555
556 return $items;
557 }
558
559 /**
560 * Remove items user restriction on language field
561 *
562 * Used by TcaSelectItems and TcaSelectTreeItems data providers
563 *
564 * @param array $result Result array
565 * @param string $fieldName Current handle field name
566 * @param array $items Incoming items
567 * @return array Modified item array
568 */
569 protected function removeItemsByUserLanguageFieldRestriction(array $result, $fieldName, array $items)
570 {
571 // Guard clause returns if not a language field is handled
572 if (empty($result['processedTca']['ctrl']['languageField'])
573 || $result['processedTca']['ctrl']['languageField'] !== $fieldName
574 ) {
575 return $items;
576 }
577
578 $backendUser = $this->getBackendUser();
579 foreach ($items as $key => $itemValues) {
580 if (!$backendUser->checkLanguageAccess($itemValues[1])) {
581 unset($items[$key]);
582 }
583 }
584
585 return $items;
586 }
587
588 /**
589 * Remove items by user restriction on authMode items
590 *
591 * Used by TcaSelectItems and TcaSelectTreeItems data providers
592 *
593 * @param array $result Result array
594 * @param string $fieldName Current handle field name
595 * @param array $items Incoming items
596 * @return array Modified item array
597 */
598 protected function removeItemsByUserAuthMode(array $result, $fieldName, array $items)
599 {
600 // Guard clause returns early if no authMode field is configured
601 if (!isset($result['processedTca']['columns'][$fieldName]['config']['authMode'])
602 || !is_string($result['processedTca']['columns'][$fieldName]['config']['authMode'])
603 ) {
604 return $items;
605 }
606
607 $backendUser = $this->getBackendUser();
608 $authMode = $result['processedTca']['columns'][$fieldName]['config']['authMode'];
609 foreach ($items as $key => $itemValues) {
610 // @todo: checkAuthMode() uses $GLOBAL access for "individual" authMode - get rid of this
611 if (!$backendUser->checkAuthMode($result['tableName'], $fieldName, $itemValues[1], $authMode)) {
612 unset($items[$key]);
613 }
614 }
615
616 return $items;
617 }
618
619 /**
620 * Remove items if doktype is handled for non admin users
621 *
622 * Used by TcaSelectItems and TcaSelectTreeItems data providers
623 *
624 * @param array $result Result array
625 * @param string $fieldName Current handle field name
626 * @param array $items Incoming items
627 * @return array Modified item array
628 */
629 protected function removeItemsByDoktypeUserRestriction(array $result, $fieldName, array $items)
630 {
631 $table = $result['tableName'];
632 $backendUser = $this->getBackendUser();
633 // Guard clause returns if not correct table and field or if user is admin
634 if ($table !== 'pages' && $table !== 'pages_language_overlay'
635 || $fieldName !== 'doktype' || $backendUser->isAdmin()
636 ) {
637 return $items;
638 }
639
640 $allowedPageTypes = $backendUser->groupData['pagetypes_select'];
641 foreach ($items as $key => $itemValues) {
642 if (!GeneralUtility::inList($allowedPageTypes, $itemValues[1])) {
643 unset($items[$key]);
644 }
645 }
646
647 return $items;
648 }
649
650 /**
651 * Returns an array with the exclude fields as defined in TCA and FlexForms
652 * Used for listing the exclude fields in be_groups forms.
653 *
654 * @return array Array of arrays with excludeFields (fieldName, table:fieldName) from TCA
655 * and FlexForms (fieldName, table:extKey;sheetName;fieldName)
656 */
657 protected function getExcludeFields()
658 {
659 $languageService = $this->getLanguageService();
660 $finalExcludeArray = [];
661
662 // Fetch translations for table names
663 $tableToTranslation = [];
664 // All TCA keys
665 foreach ($GLOBALS['TCA'] as $table => $conf) {
666 $tableToTranslation[$table] = $languageService->sL($conf['ctrl']['title']);
667 }
668 // Sort by translations
669 asort($tableToTranslation);
670 foreach ($tableToTranslation as $table => $translatedTable) {
671 $excludeArrayTable = [];
672
673 // All field names configured and not restricted to admins
674 if (is_array($GLOBALS['TCA'][$table]['columns'])
675 && empty($GLOBALS['TCA'][$table]['ctrl']['adminOnly'])
676 && (empty($GLOBALS['TCA'][$table]['ctrl']['rootLevel']) || !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']))
677 ) {
678 foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $_) {
679 if ($GLOBALS['TCA'][$table]['columns'][$field]['exclude']) {
680 // Get human readable names of fields
681 $translatedField = $languageService->sL($GLOBALS['TCA'][$table]['columns'][$field]['label']);
682 // Add entry, key 'labels' needed for sorting
683 $excludeArrayTable[] = [
684 'labels' => $translatedTable . ':' . $translatedField,
685 'sectionHeader' => $translatedTable,
686 'table' => $table,
687 'tableField' => $field,
688 'fieldName' => $field,
689 'fullField' => $field,
690 'fieldLabel' => $translatedField,
691 'origin' => 'tca',
692 ];
693 }
694 }
695 }
696 // All FlexForm fields
697 $flexFormArray = $this->getRegisteredFlexForms($table);
698 foreach ($flexFormArray as $tableField => $flexForms) {
699 // Prefix for field label, e.g. "Plugin Options:"
700 $labelPrefix = '';
701 if (!empty($GLOBALS['TCA'][$table]['columns'][$tableField]['label'])) {
702 $labelPrefix = $languageService->sL($GLOBALS['TCA'][$table]['columns'][$tableField]['label']);
703 }
704 // Get all sheets and title
705 foreach ($flexForms as $extIdent => $extConf) {
706 $extTitle = $languageService->sl(trim($extConf['title']));
707 // Get all fields in sheet
708 foreach ($extConf['ds']['sheets'] as $sheetName => $sheet) {
709 if (empty($sheet['ROOT']['el']) || !is_array($sheet['ROOT']['el'])) {
710 continue;
711 }
712 foreach ($sheet['ROOT']['el'] as $pluginFieldName => $field) {
713 // Use only fields that have exclude flag set
714 if (empty($field['TCEforms']['exclude'])) {
715 continue;
716 }
717 $fieldLabel = !empty($field['TCEforms']['label'])
718 ? $languageService->sL($field['TCEforms']['label'])
719 : $pluginFieldName;
720 $excludeArrayTable[] = [
721 'labels' => trim($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent, ': ') . ':' . $fieldLabel,
722 'sectionHeader' => trim(($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent), ':'),
723 'table' => $table,
724 'tableField' => $tableField,
725 'extIdent' => $extIdent,
726 'fieldName' => $pluginFieldName,
727 'fullField' => $tableField . ';' . $extIdent . ';' . $sheetName . ';' . $pluginFieldName,
728 'fieldLabel' => $fieldLabel,
729 'origin' => 'flexForm',
730 ];
731 }
732 }
733 }
734 }
735 // Sort fields by the translated value
736 if (!empty($excludeArrayTable)) {
737 usort($excludeArrayTable, function (array $array1, array $array2) {
738 $array1 = reset($array1);
739 $array2 = reset($array2);
740 if (is_string($array1) && is_string($array2)) {
741 return strcasecmp($array1, $array2);
742 }
743 return 0;
744 });
745 $finalExcludeArray = array_merge($finalExcludeArray, $excludeArrayTable);
746 }
747 }
748
749 return $finalExcludeArray;
750 }
751
752 /**
753 * Returns all registered FlexForm definitions with title and fields
754 *
755 * @param string $table Table to handle
756 * @return array Data structures with speaking extension title
757 */
758 protected function getRegisteredFlexForms($table)
759 {
760 if (empty($table) || empty($GLOBALS['TCA'][$table]['columns'])) {
761 return [];
762 }
763 $flexForms = [];
764 foreach ($GLOBALS['TCA'][$table]['columns'] as $tableField => $fieldConf) {
765 if (!empty($fieldConf['config']['type']) && !empty($fieldConf['config']['ds']) && $fieldConf['config']['type'] == 'flex') {
766 $flexForms[$tableField] = [];
767 // Get pointer fields
768 $pointerFields = !empty($fieldConf['config']['ds_pointerField']) ? $fieldConf['config']['ds_pointerField'] : 'list_type,CType,default';
769 $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
770 // Get FlexForms
771 foreach ($fieldConf['config']['ds'] as $flexFormKey => $dataStructure) {
772 // Get extension identifier (uses second value if it's not empty, "list" or "*", else first one)
773 $identFields = GeneralUtility::trimExplode(',', $flexFormKey);
774 $extIdent = $identFields[0];
775 if (!empty($identFields[1]) && $identFields[1] !== 'list' && $identFields[1] !== '*') {
776 $extIdent = $identFields[1];
777 }
778 // Load external file references
779 if (!is_array($dataStructure)) {
780 $file = GeneralUtility::getFileAbsFileName(str_ireplace('FILE:', '', $dataStructure));
781 if ($file && @is_file($file)) {
782 $dataStructure = GeneralUtility::getUrl($file);
783 }
784 $dataStructure = GeneralUtility::xml2array($dataStructure);
785 if (!is_array($dataStructure)) {
786 continue;
787 }
788 }
789 // Get flexform content
790 $dataStructure = GeneralUtility::resolveAllSheetsInDS($dataStructure);
791 if (empty($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
792 continue;
793 }
794 // Use DS pointer to get extension title from TCA
795 // @todo: I don't understand this code ... does it make sense at all?
796 $title = $extIdent;
797 $keyFields = GeneralUtility::trimExplode(',', $flexFormKey);
798 foreach ($pointerFields as $pointerKey => $pointerName) {
799 if (empty($keyFields[$pointerKey])
800 || $keyFields[$pointerKey] === '*'
801 || $keyFields[$pointerKey] === 'list'
802 || $keyFields[$pointerKey] === 'default'
803 ) {
804 continue;
805 }
806 if (!empty($GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'])) {
807 $items = $GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'];
808 if (!is_array($items)) {
809 continue;
810 }
811 foreach ($items as $itemConf) {
812 if (!empty($itemConf[0]) && !empty($itemConf[1]) && $itemConf[1] == $keyFields[$pointerKey]) {
813 $title = $itemConf[0];
814 break 2;
815 }
816 }
817 }
818 }
819 $flexForms[$tableField][$extIdent] = [
820 'title' => $title,
821 'ds' => $dataStructure
822 ];
823 }
824 }
825 }
826 return $flexForms;
827 }
828
829 /**
830 * Returns an array with explicit Allow/Deny fields.
831 * Used for listing these field/value pairs in be_groups forms
832 *
833 * @return array Array with information from all of $GLOBALS['TCA']
834 */
835 protected function getExplicitAuthFieldValues()
836 {
837 $languageService = static::getLanguageService();
838 $adLabel = [
839 'ALLOW' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.allow'),
840 'DENY' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.deny')
841 ];
842 $allowDenyOptions = [];
843 foreach ($GLOBALS['TCA'] as $table => $_) {
844 // All field names configured:
845 if (is_array($GLOBALS['TCA'][$table]['columns'])) {
846 foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $__) {
847 $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
848 if ($fieldConfig['type'] === 'select' && $fieldConfig['authMode']) {
849 // Check for items
850 if (is_array($fieldConfig['items'])) {
851 // Get Human Readable names of fields and table:
852 $allowDenyOptions[$table . ':' . $field]['tableFieldLabel'] =
853 $languageService->sL($GLOBALS['TCA'][$table]['ctrl']['title']) . ': '
854 . $languageService->sL($GLOBALS['TCA'][$table]['columns'][$field]['label']);
855 foreach ($fieldConfig['items'] as $iVal) {
856 // Values '' is not controlled by this setting.
857 if ((string)$iVal[1] !== '') {
858 // Find iMode
859 $iMode = '';
860 switch ((string)$fieldConfig['authMode']) {
861 case 'explicitAllow':
862 $iMode = 'ALLOW';
863 break;
864 case 'explicitDeny':
865 $iMode = 'DENY';
866 break;
867 case 'individual':
868 if ($iVal[4] === 'EXPL_ALLOW') {
869 $iMode = 'ALLOW';
870 } elseif ($iVal[4] === 'EXPL_DENY') {
871 $iMode = 'DENY';
872 }
873 break;
874 }
875 // Set iMode
876 if ($iMode) {
877 $allowDenyOptions[$table . ':' . $field]['items'][$iVal[1]] = [
878 $iMode,
879 $languageService->sL($iVal[0]),
880 $adLabel[$iMode]
881 ];
882 }
883 }
884 }
885 }
886 }
887 }
888 }
889 }
890 return $allowDenyOptions;
891 }
892
893 /**
894 * Build query to fetch foreign records. Helper method of
895 * addItemsFromForeignTable(), do not call otherwise.
896 *
897 * @param array $result Result array
898 * @param string $localFieldName Current handle field name
899 * @return array Query array ready to be executed via Database->exec_SELECT_queryArray()
900 */
901 protected function buildForeignTableQuery(array $result, $localFieldName)
902 {
903 $backendUser = $this->getBackendUser();
904
905 $foreignTableName = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table'];
906 $foreignTableClauseArray = $this->processForeignTableClause($result, $foreignTableName, $localFieldName);
907
908 $queryArray = [];
909 $queryArray['SELECT'] = BackendUtility::getCommonSelectFields($foreignTableName, $foreignTableName . '.');
910
911 // rootLevel = -1 means that elements can be on the rootlevel OR on any page (pid!=-1)
912 // rootLevel = 0 means that elements are not allowed on root level
913 // rootLevel = 1 means that elements are only on the root level (pid=0)
914 $rootLevel = 0;
915 if (isset($GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'])) {
916 $rootLevel = $GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'];
917 }
918 $deleteClause = BackendUtility::deleteClause($foreignTableName);
919 if ($rootLevel == 1 || $rootLevel == -1) {
920 $pidWhere = $foreignTableName . '.pid' . (($rootLevel == -1) ? '<>-1' : '=0');
921 $queryArray['FROM'] = $foreignTableName;
922 $queryArray['WHERE'] = $pidWhere . $deleteClause . $foreignTableClauseArray['WHERE'];
923 } else {
924 $pageClause = $backendUser->getPagePermsClause(1);
925 if ($foreignTableName === 'pages') {
926 $queryArray['FROM'] = 'pages';
927 $queryArray['WHERE'] = '1=1' . $deleteClause . ' AND' . $pageClause . $foreignTableClauseArray['WHERE'];
928 } else {
929 $queryArray['FROM'] = $foreignTableName . ', pages';
930 $queryArray['WHERE'] = 'pages.uid=' . $foreignTableName . '.pid AND pages.deleted=0'
931 . $deleteClause . ' AND' . $pageClause . $foreignTableClauseArray['WHERE'];
932 }
933 }
934
935 $queryArray['GROUPBY'] = $foreignTableClauseArray['GROUPBY'];
936 $queryArray['ORDERBY'] = $foreignTableClauseArray['ORDERBY'];
937 $queryArray['LIMIT'] = $foreignTableClauseArray['LIMIT'];
938
939 return $queryArray;
940 }
941
942 /**
943 * Replace markers in a where clause from TCA foreign_table_where
944 *
945 * ###REC_FIELD_[field name]###
946 * ###THIS_UID### - is current element uid (zero if new).
947 * ###CURRENT_PID### - is the current page id (pid of the record).
948 * ###SITEROOT###
949 * ###PAGE_TSCONFIG_ID### - a value you can set from Page TSconfig dynamically.
950 * ###PAGE_TSCONFIG_IDLIST### - a value you can set from Page TSconfig dynamically.
951 * ###PAGE_TSCONFIG_STR### - a value you can set from Page TSconfig dynamically.
952 *
953 * @param array $result Result array
954 * @param string $foreignTableName Name of foreign table
955 * @param string $localFieldName Current handle field name
956 * @return array Query parts with keys WHERE, ORDERBY, GROUPBY, LIMIT
957 */
958 protected function processForeignTableClause(array $result, $foreignTableName, $localFieldName)
959 {
960 $database = $this->getDatabaseConnection();
961 $localTable = $result['tableName'];
962
963 $foreignTableClause = '';
964 if (!empty($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
965 && is_string($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
966 ) {
967 $foreignTableClause = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'];
968 // Replace possible markers in query
969 if (strstr($foreignTableClause, '###REC_FIELD_')) {
970 // " AND table.field='###REC_FIELD_field1###' AND ..." -> array(" AND table.field='", "field1###' AND ...")
971 $whereClauseParts = explode('###REC_FIELD_', $foreignTableClause);
972 foreach ($whereClauseParts as $key => $value) {
973 if ($key !== 0) {
974 // "field1###' AND ..." -> array("field1", "' AND ...")
975 $whereClauseSubParts = explode('###', $value, 2);
976 // @todo: Throw exception if there is no value? What happens for NEW records?
977 $databaseRowKey = empty($result['flexParentDatabaseRow']) ? 'databaseRow' : 'flexParentDatabaseRow';
978 $rowFieldValue = isset($result[$databaseRowKey][$whereClauseSubParts[0]]) ? $result[$databaseRowKey][$whereClauseSubParts[0]] : '';
979 if (is_array($rowFieldValue)) {
980 // If a select or group field is used here, it may have been processed already and
981 // is now an array. Use first selected value in this case.
982 $rowFieldValue = $rowFieldValue[0];
983 }
984 if (substr($whereClauseParts[0], -1) === '\'' && $whereClauseSubParts[1][0] === '\'') {
985 $whereClauseParts[$key] = $database->quoteStr($rowFieldValue, $foreignTableName) . $whereClauseSubParts[1];
986 } else {
987 $whereClauseParts[$key] = $database->fullQuoteStr($rowFieldValue, $foreignTableName) . $whereClauseSubParts[1];
988 }
989 }
990 }
991 $foreignTableClause = implode('', $whereClauseParts);
992 }
993
994 $siteRootUid = 0;
995 foreach ($result['rootline'] as $rootlinePage) {
996 if (!empty($rootlinePage['is_siteroot'])) {
997 $siteRootUid = (int)$rootlinePage['uid'];
998 break;
999 }
1000 }
1001
1002 $pageTsConfigId = 0;
1003 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_ID']) {
1004 $pageTsConfigId = (int)$result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_ID'];
1005 }
1006 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID']) {
1007 $pageTsConfigId = (int)$result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID'];
1008 }
1009
1010 $pageTsConfigIdList = 0;
1011 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_IDLIST']) {
1012 $pageTsConfigIdList = $result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_IDLIST'];
1013 }
1014 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST']) {
1015 $pageTsConfigIdList = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST'];
1016 }
1017 $pageTsConfigIdListArray = GeneralUtility::trimExplode(',', $pageTsConfigIdList, true);
1018 $pageTsConfigIdList = [];
1019 foreach ($pageTsConfigIdListArray as $pageTsConfigIdListElement) {
1020 if (MathUtility::canBeInterpretedAsInteger($pageTsConfigIdListElement)) {
1021 $pageTsConfigIdList[] = (int)$pageTsConfigIdListElement;
1022 }
1023 }
1024 $pageTsConfigIdList = implode(',', $pageTsConfigIdList);
1025
1026 $pageTsConfigString = '';
1027 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_STR']) {
1028 $pageTsConfigString = $result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_STR'];
1029 }
1030 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR']) {
1031 $pageTsConfigString = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR'];
1032 $pageTsConfigString = $database->quoteStr($pageTsConfigString, $foreignTableName);
1033 }
1034
1035 $foreignTableClause = str_replace(
1036 [
1037 '###CURRENT_PID###',
1038 '###THIS_UID###',
1039 '###SITEROOT###',
1040 '###PAGE_TSCONFIG_ID###',
1041 '###PAGE_TSCONFIG_IDLIST###',
1042 '###PAGE_TSCONFIG_STR###'
1043 ],
1044 [
1045 (int)$result['effectivePid'],
1046 (int)$result['databaseRow']['uid'],
1047 $siteRootUid,
1048 $pageTsConfigId,
1049 $pageTsConfigIdList,
1050 $pageTsConfigString
1051 ],
1052 $foreignTableClause
1053 );
1054 }
1055
1056 // Split the clause into an array with keys WHERE, GROUPBY, ORDERBY, LIMIT
1057 // Prepend a space to make sure "[[:space:]]+" will find a space there for the first element.
1058 $foreignTableClause = ' ' . $foreignTableClause;
1059 $foreignTableClauseArray = [
1060 'WHERE' => '',
1061 'GROUPBY' => '',
1062 'ORDERBY' => '',
1063 'LIMIT' => '',
1064 ];
1065 // Find LIMIT
1066 $reg = [];
1067 if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1068 $foreignTableClauseArray['LIMIT'] = trim($reg[2]);
1069 $foreignTableClause = $reg[1];
1070 }
1071 // Find ORDER BY
1072 $reg = [];
1073 if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1074 $foreignTableClauseArray['ORDERBY'] = trim($reg[2]);
1075 $foreignTableClause = $reg[1];
1076 }
1077 // Find GROUP BY
1078 $reg = [];
1079 if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1080 $foreignTableClauseArray['GROUPBY'] = trim($reg[2]);
1081 $foreignTableClause = $reg[1];
1082 }
1083 // Rest is assumed to be "WHERE" clause
1084 $foreignTableClauseArray['WHERE'] = $foreignTableClause;
1085
1086 return $foreignTableClauseArray;
1087 }
1088
1089 /**
1090 * Convert the current database values into an array
1091 *
1092 * @param array $row database row
1093 * @param string $fieldName fieldname to process
1094 * @return array
1095 */
1096 protected function processDatabaseFieldValue(array $row, $fieldName)
1097 {
1098 $currentDatabaseValues = array_key_exists($fieldName, $row)
1099 ? $row[$fieldName]
1100 : '';
1101 return GeneralUtility::trimExplode(',', $currentDatabaseValues, true);
1102 }
1103
1104 /**
1105 * Validate and sanitize database row values of the select field with the given name.
1106 * Creates an array out of databaseRow[selectField] values.
1107 *
1108 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1109 *
1110 * @param array $result The current result array.
1111 * @param string $fieldName Name of the current select field.
1112 * @param array $staticValues Array with statically defined items, item value is used as array key.
1113 * @return array
1114 */
1115 protected function processSelectFieldValue(array $result, $fieldName, array $staticValues)
1116 {
1117 $fieldConfig = $result['processedTca']['columns'][$fieldName];
1118
1119 $currentDatabaseValueArray = array_key_exists($fieldName, $result['databaseRow']) ? $result['databaseRow'][$fieldName] : [];
1120 $newDatabaseValueArray = [];
1121
1122 // Add all values that were defined by static methods and do not come from the relation
1123 // e.g. TCA, TSconfig, itemProcFunc etc.
1124 foreach ($currentDatabaseValueArray as $value) {
1125 if (isset($staticValues[$value])) {
1126 $newDatabaseValueArray[] = $value;
1127 }
1128 }
1129
1130 if (isset($fieldConfig['config']['foreign_table']) && !empty($fieldConfig['config']['foreign_table'])) {
1131 /** @var RelationHandler $relationHandler */
1132 $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
1133 $relationHandler->registerNonTableValues = !empty($fieldConfig['config']['allowNonIdValues']);
1134 if (!empty($fieldConfig['config']['MM']) && $result['command'] !== 'new') {
1135 // MM relation
1136 $relationHandler->start(
1137 implode(',', $currentDatabaseValueArray),
1138 $fieldConfig['config']['foreign_table'],
1139 $fieldConfig['config']['MM'],
1140 $result['databaseRow']['uid'],
1141 $result['tableName'],
1142 $fieldConfig['config']
1143 );
1144 } else {
1145 // Non MM relation
1146 // If not dealing with MM relations, use default live uid, not versioned uid for record relations
1147 $relationHandler->start(
1148 implode(',', $currentDatabaseValueArray),
1149 $fieldConfig['config']['foreign_table'],
1150 '',
1151 $this->getLiveUid($result),
1152 $result['tableName'],
1153 $fieldConfig['config']
1154 );
1155 }
1156 $newDatabaseValueArray = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
1157 }
1158
1159 return array_unique($newDatabaseValueArray);
1160 }
1161
1162 /**
1163 * Translate the item labels
1164 *
1165 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1166 *
1167 * @param array $result Result array
1168 * @param array $itemArray Items
1169 * @param string $table
1170 * @param string $fieldName
1171 * @return array
1172 */
1173 public function translateLabels(array $result, array $itemArray, $table, $fieldName)
1174 {
1175 $languageService = $this->getLanguageService();
1176
1177 foreach ($itemArray as $key => $item) {
1178 if (!isset($dynamicItems[$key])) {
1179 $staticValues[$item[1]] = $item;
1180 }
1181 if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1182 && !empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1183 ) {
1184 $label = $languageService->sL($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]]);
1185 } else {
1186 $label = $languageService->sL(trim($item[0]));
1187 }
1188 $value = strlen((string)$item[1]) > 0 ? $item[1] : '';
1189 $icon = $item[2] ?: null;
1190 $helpText = $item[3] ?: null;
1191 $itemArray[$key] = [
1192 $label,
1193 $value,
1194 $icon,
1195 $helpText
1196 ];
1197 }
1198
1199 return $itemArray;
1200 }
1201
1202 /**
1203 * Sanitize incoming item array
1204 *
1205 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1206 *
1207 * @param mixed $itemArray
1208 * @param string $tableName
1209 * @param string $fieldName
1210 * @throws \UnexpectedValueException
1211 * @return array
1212 */
1213 public function sanitizeItemArray($itemArray, $tableName, $fieldName)
1214 {
1215 if (!is_array($itemArray)) {
1216 $itemArray = [];
1217 }
1218 foreach ($itemArray as $item) {
1219 if (!is_array($item)) {
1220 throw new \UnexpectedValueException(
1221 'An item in field ' . $fieldName . ' of table ' . $tableName . ' is not an array as expected',
1222 1439288036
1223 );
1224 }
1225 }
1226
1227 return $itemArray;
1228 }
1229
1230 /**
1231 * Make sure maxitems is always filled with a valid integer value.
1232 *
1233 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1234 *
1235 * @param mixed $maxItems
1236 * @return int
1237 */
1238 public function sanitizeMaxItems($maxItems)
1239 {
1240 if (!empty($maxItems)
1241 && (int)$maxItems > 1
1242 ) {
1243 $maxItems = (int)$maxItems;
1244 } else {
1245 $maxItems = 1;
1246 }
1247
1248 return $maxItems;
1249 }
1250
1251 /**
1252 * Gets the record uid of the live default record. If already
1253 * pointing to the live record, the submitted record uid is returned.
1254 *
1255 * @param array $result Result array
1256 * @return int
1257 * @throws \UnexpectedValueException
1258 */
1259 protected function getLiveUid(array $result)
1260 {
1261 $table = $result['tableName'];
1262 $row = $result['databaseRow'];
1263 $uid = $row['uid'];
1264 if (!empty($result['processedTca']['ctrl']['versioningWS'])
1265 && $result['pid'] === -1
1266 ) {
1267 if (empty($row['t3ver_oid'])) {
1268 throw new \UnexpectedValueException(
1269 'No t3ver_oid found for record ' . $row['uid'] . ' on table ' . $table,
1270 1440066481
1271 );
1272 }
1273 $uid = $row['t3ver_oid'];
1274 }
1275 return $uid;
1276 }
1277
1278 /**
1279 * Determine the static values in the item array
1280 *
1281 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1282 *
1283 * @param array $itemArray All item records for the select field
1284 * @param array $dynamicItemArray Item records from dynamic sources
1285 * @return array
1286 */
1287 public function getStaticValues($itemArray, $dynamicItemArray)
1288 {
1289 $staticValues = [];
1290 foreach ($itemArray as $key => $item) {
1291 if (!isset($dynamicItemArray[$key])) {
1292 $staticValues[$item[1]] = $item;
1293 }
1294 }
1295 return $staticValues;
1296 }
1297
1298 /**
1299 * @return LanguageService
1300 */
1301 protected function getLanguageService()
1302 {
1303 return $GLOBALS['LANG'];
1304 }
1305
1306 /**
1307 * @return DatabaseConnection
1308 */
1309 protected function getDatabaseConnection()
1310 {
1311 return $GLOBALS['TYPO3_DB'];
1312 }
1313
1314 /**
1315 * @return BackendUserAuthentication
1316 */
1317 protected function getBackendUser()
1318 {
1319 return $GLOBALS['BE_USER'];
1320 }
1321 }