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