5fee098f33798c9436fae4d49d6f2b3509b3e794
[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 */
409 protected function addItemsFromForeignTable(array $result, $fieldName, array $items)
410 {
411 // Guard
412 if (empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
413 || !is_string($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
414 ) {
415 return $items;
416 }
417
418 $languageService = $this->getLanguageService();
419 $database = $this->getDatabaseConnection();
420
421 $foreignTable = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
422 $foreignTableQueryArray = $this->buildForeignTableQuery($result, $fieldName);
423 $queryResource = $database->exec_SELECT_queryArray($foreignTableQueryArray);
424
425 // Early return on error with flash message
426 $databaseError = $database->sql_error();
427 if (!empty($databaseError)) {
428 $msg = $databaseError . '. ';
429 $msg .= $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch');
430 $msgTitle = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch_title');
431 /** @var $flashMessage FlashMessage */
432 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $msg, $msgTitle, FlashMessage::ERROR, true);
433 /** @var $flashMessageService FlashMessageService */
434 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
435 /** @var $defaultFlashMessageQueue FlashMessageQueue */
436 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
437 $defaultFlashMessageQueue->enqueue($flashMessage);
438 $database->sql_free_result($queryResource);
439 return $items;
440 }
441
442 $labelPrefix = '';
443 if (!empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'])) {
444 $labelPrefix = $result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'];
445 $labelPrefix = $languageService->sL($labelPrefix);
446 }
447 $iconFieldName = '';
448 if (!empty($result['processedTca']['ctrl']['selicon_field'])) {
449 $iconFieldName = $result['processedTca']['ctrl']['selicon_field'];
450 }
451 $iconPath = '';
452 if (!empty($result['processedTca']['ctrl']['selicon_field_path'])) {
453 $iconPath = $result['processedTca']['ctrl']['selicon_field_path'];
454 }
455
456 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
457
458 while ($foreignRow = $database->sql_fetch_assoc($queryResource)) {
459 BackendUtility::workspaceOL($foreignTable, $foreignRow);
460 if (is_array($foreignRow)) {
461 // Prepare the icon if available:
462 if ($iconFieldName && $iconPath && $foreignRow[$iconFieldName]) {
463 $iParts = GeneralUtility::trimExplode(',', $foreignRow[$iconFieldName], true);
464 $icon = '../' . $iconPath . '/' . trim($iParts[0]);
465 } else {
466 $icon = $iconFactory->mapRecordTypeToIconIdentifier($foreignTable, $foreignRow);
467 }
468 // Add the item
469 $items[] = [
470 $labelPrefix . htmlspecialchars(BackendUtility::getRecordTitle($foreignTable, $foreignRow)),
471 $foreignRow['uid'],
472 $icon
473 ];
474 }
475 }
476
477 $database->sql_free_result($queryResource);
478
479 return $items;
480 }
481
482 /**
483 * Remove items using "keepItems" pageTsConfig
484 *
485 * Used by TcaSelectItems and TcaSelectTreeItems data providers
486 *
487 * @param array $result Result array
488 * @param string $fieldName Current handle field name
489 * @param array $items Incoming items
490 * @return array Modified item array
491 */
492 protected function removeItemsByKeepItemsPageTsConfig(array $result, $fieldName, array $items)
493 {
494 $table = $result['tableName'];
495 if (!isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
496 || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
497 ) {
498 return $items;
499 }
500
501 // If keepItems is set but is an empty list all current items get removed
502 if (empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
503 && $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'] !== '0') {
504 return [];
505 }
506
507 return ArrayUtility::keepItemsInArray(
508 $items,
509 $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'],
510 function ($value) {
511 return $value[1];
512 }
513 );
514 }
515
516 /**
517 * Remove items using "removeItems" pageTsConfig
518 *
519 * Used by TcaSelectItems and TcaSelectTreeItems data providers
520 *
521 * @param array $result Result array
522 * @param string $fieldName Current handle field name
523 * @param array $items Incoming items
524 * @return array Modified item array
525 */
526 protected function removeItemsByRemoveItemsPageTsConfig(array $result, $fieldName, array $items)
527 {
528 $table = $result['tableName'];
529 if (empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
530 || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
531 ) {
532 return $items;
533 }
534
535 $removeItems = GeneralUtility::trimExplode(
536 ',',
537 $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'],
538 true
539 );
540 foreach ($items as $key => $itemValues) {
541 if (in_array($itemValues[1], $removeItems)) {
542 unset($items[$key]);
543 }
544 }
545
546 return $items;
547 }
548
549 /**
550 * Remove items user restriction on language field
551 *
552 * Used by TcaSelectItems and TcaSelectTreeItems data providers
553 *
554 * @param array $result Result array
555 * @param string $fieldName Current handle field name
556 * @param array $items Incoming items
557 * @return array Modified item array
558 */
559 protected function removeItemsByUserLanguageFieldRestriction(array $result, $fieldName, array $items)
560 {
561 // Guard clause returns if not a language field is handled
562 if (empty($result['processedTca']['ctrl']['languageField'])
563 || $result['processedTca']['ctrl']['languageField'] !== $fieldName
564 ) {
565 return $items;
566 }
567
568 $backendUser = $this->getBackendUser();
569 foreach ($items as $key => $itemValues) {
570 if (!$backendUser->checkLanguageAccess($itemValues[1])) {
571 unset($items[$key]);
572 }
573 }
574
575 return $items;
576 }
577
578 /**
579 * Remove items by user restriction on authMode items
580 *
581 * Used by TcaSelectItems and TcaSelectTreeItems data providers
582 *
583 * @param array $result Result array
584 * @param string $fieldName Current handle field name
585 * @param array $items Incoming items
586 * @return array Modified item array
587 */
588 protected function removeItemsByUserAuthMode(array $result, $fieldName, array $items)
589 {
590 // Guard clause returns early if no authMode field is configured
591 if (!isset($result['processedTca']['columns'][$fieldName]['config']['authMode'])
592 || !is_string($result['processedTca']['columns'][$fieldName]['config']['authMode'])
593 ) {
594 return $items;
595 }
596
597 $backendUser = $this->getBackendUser();
598 $authMode = $result['processedTca']['columns'][$fieldName]['config']['authMode'];
599 foreach ($items as $key => $itemValues) {
600 // @todo: checkAuthMode() uses $GLOBAL access for "individual" authMode - get rid of this
601 if (!$backendUser->checkAuthMode($result['tableName'], $fieldName, $itemValues[1], $authMode)) {
602 unset($items[$key]);
603 }
604 }
605
606 return $items;
607 }
608
609 /**
610 * Remove items if doktype is handled for non admin users
611 *
612 * Used by TcaSelectItems and TcaSelectTreeItems data providers
613 *
614 * @param array $result Result array
615 * @param string $fieldName Current handle field name
616 * @param array $items Incoming items
617 * @return array Modified item array
618 */
619 protected function removeItemsByDoktypeUserRestriction(array $result, $fieldName, array $items)
620 {
621 $table = $result['tableName'];
622 $backendUser = $this->getBackendUser();
623 // Guard clause returns if not correct table and field or if user is admin
624 if ($table !== 'pages' && $table !== 'pages_language_overlay'
625 || $fieldName !== 'doktype' || $backendUser->isAdmin()
626 ) {
627 return $items;
628 }
629
630 $allowedPageTypes = $backendUser->groupData['pagetypes_select'];
631 foreach ($items as $key => $itemValues) {
632 if (!GeneralUtility::inList($allowedPageTypes, $itemValues[1])) {
633 unset($items[$key]);
634 }
635 }
636
637 return $items;
638 }
639
640 /**
641 * Returns an array with the exclude fields as defined in TCA and FlexForms
642 * Used for listing the exclude fields in be_groups forms.
643 *
644 * @return array Array of arrays with excludeFields (fieldName, table:fieldName) from TCA
645 * and FlexForms (fieldName, table:extKey;sheetName;fieldName)
646 */
647 protected function getExcludeFields()
648 {
649 $languageService = $this->getLanguageService();
650 $finalExcludeArray = [];
651
652 // Fetch translations for table names
653 $tableToTranslation = [];
654 // All TCA keys
655 foreach ($GLOBALS['TCA'] as $table => $conf) {
656 $tableToTranslation[$table] = $languageService->sL($conf['ctrl']['title']);
657 }
658 // Sort by translations
659 asort($tableToTranslation);
660 foreach ($tableToTranslation as $table => $translatedTable) {
661 $excludeArrayTable = [];
662
663 // All field names configured and not restricted to admins
664 if (is_array($GLOBALS['TCA'][$table]['columns'])
665 && empty($GLOBALS['TCA'][$table]['ctrl']['adminOnly'])
666 && (empty($GLOBALS['TCA'][$table]['ctrl']['rootLevel']) || !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']))
667 ) {
668 foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $_) {
669 if ($GLOBALS['TCA'][$table]['columns'][$field]['exclude']) {
670 // Get human readable names of fields
671 $translatedField = $languageService->sL($GLOBALS['TCA'][$table]['columns'][$field]['label']);
672 // Add entry, key 'labels' needed for sorting
673 $excludeArrayTable[] = [
674 'labels' => $translatedTable . ':' . $translatedField,
675 'sectionHeader' => $translatedTable,
676 'table' => $table,
677 'tableField' => $field,
678 'fieldName' => $field,
679 'fullField' => $field,
680 'fieldLabel' => $translatedField,
681 'origin' => 'tca',
682 ];
683 }
684 }
685 }
686 // All FlexForm fields
687 $flexFormArray = $this->getRegisteredFlexForms($table);
688 foreach ($flexFormArray as $tableField => $flexForms) {
689 // Prefix for field label, e.g. "Plugin Options:"
690 $labelPrefix = '';
691 if (!empty($GLOBALS['TCA'][$table]['columns'][$tableField]['label'])) {
692 $labelPrefix = $languageService->sL($GLOBALS['TCA'][$table]['columns'][$tableField]['label']);
693 }
694 // Get all sheets and title
695 foreach ($flexForms as $extIdent => $extConf) {
696 $extTitle = $languageService->sL(trim($extConf['title']));
697 // Get all fields in sheet
698 foreach ($extConf['ds']['sheets'] as $sheetName => $sheet) {
699 if (empty($sheet['ROOT']['el']) || !is_array($sheet['ROOT']['el'])) {
700 continue;
701 }
702 foreach ($sheet['ROOT']['el'] as $pluginFieldName => $field) {
703 // Use only fields that have exclude flag set
704 if (empty($field['TCEforms']['exclude'])) {
705 continue;
706 }
707 $fieldLabel = !empty($field['TCEforms']['label'])
708 ? $languageService->sL($field['TCEforms']['label'])
709 : $pluginFieldName;
710 $excludeArrayTable[] = [
711 'labels' => trim($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent, ': ') . ':' . $fieldLabel,
712 'sectionHeader' => trim(($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent), ':'),
713 'table' => $table,
714 'tableField' => $tableField,
715 'extIdent' => $extIdent,
716 'fieldName' => $pluginFieldName,
717 'fullField' => $tableField . ';' . $extIdent . ';' . $sheetName . ';' . $pluginFieldName,
718 'fieldLabel' => $fieldLabel,
719 'origin' => 'flexForm',
720 ];
721 }
722 }
723 }
724 }
725 // Sort fields by the translated value
726 if (!empty($excludeArrayTable)) {
727 usort($excludeArrayTable, function (array $array1, array $array2) {
728 $array1 = reset($array1);
729 $array2 = reset($array2);
730 if (is_string($array1) && is_string($array2)) {
731 return strcasecmp($array1, $array2);
732 }
733 return 0;
734 });
735 $finalExcludeArray = array_merge($finalExcludeArray, $excludeArrayTable);
736 }
737 }
738
739 return $finalExcludeArray;
740 }
741
742 /**
743 * Returns all registered FlexForm definitions with title and fields
744 *
745 * @param string $table Table to handle
746 * @return array Data structures with speaking extension title
747 */
748 protected function getRegisteredFlexForms($table)
749 {
750 if (empty($table) || empty($GLOBALS['TCA'][$table]['columns'])) {
751 return [];
752 }
753 $flexForms = [];
754 foreach ($GLOBALS['TCA'][$table]['columns'] as $tableField => $fieldConf) {
755 if (!empty($fieldConf['config']['type']) && !empty($fieldConf['config']['ds']) && $fieldConf['config']['type'] == 'flex') {
756 $flexForms[$tableField] = [];
757 // Get pointer fields
758 $pointerFields = !empty($fieldConf['config']['ds_pointerField']) ? $fieldConf['config']['ds_pointerField'] : 'list_type,CType,default';
759 $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
760 // Get FlexForms
761 foreach ($fieldConf['config']['ds'] as $flexFormKey => $dataStructure) {
762 // Get extension identifier (uses second value if it's not empty, "list" or "*", else first one)
763 $identFields = GeneralUtility::trimExplode(',', $flexFormKey);
764 $extIdent = $identFields[0];
765 if (!empty($identFields[1]) && $identFields[1] !== 'list' && $identFields[1] !== '*') {
766 $extIdent = $identFields[1];
767 }
768 // Load external file references
769 if (!is_array($dataStructure)) {
770 $file = GeneralUtility::getFileAbsFileName(str_ireplace('FILE:', '', $dataStructure));
771 if ($file && @is_file($file)) {
772 $dataStructure = file_get_contents($file);
773 }
774 $dataStructure = GeneralUtility::xml2array($dataStructure);
775 if (!is_array($dataStructure)) {
776 continue;
777 }
778 }
779 // Get flexform content
780 $dataStructure = GeneralUtility::resolveAllSheetsInDS($dataStructure);
781 if (empty($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
782 continue;
783 }
784 // Use DS pointer to get extension title from TCA
785 // @todo: I don't understand this code ... does it make sense at all?
786 $title = $extIdent;
787 $keyFields = GeneralUtility::trimExplode(',', $flexFormKey);
788 foreach ($pointerFields as $pointerKey => $pointerName) {
789 if (empty($keyFields[$pointerKey])
790 || $keyFields[$pointerKey] === '*'
791 || $keyFields[$pointerKey] === 'list'
792 || $keyFields[$pointerKey] === 'default'
793 ) {
794 continue;
795 }
796 if (!empty($GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'])) {
797 $items = $GLOBALS['TCA'][$table]['columns'][$pointerName]['config']['items'];
798 if (!is_array($items)) {
799 continue;
800 }
801 foreach ($items as $itemConf) {
802 if (!empty($itemConf[0]) && !empty($itemConf[1]) && $itemConf[1] == $keyFields[$pointerKey]) {
803 $title = $itemConf[0];
804 break 2;
805 }
806 }
807 }
808 }
809 $flexForms[$tableField][$extIdent] = [
810 'title' => $title,
811 'ds' => $dataStructure
812 ];
813 }
814 }
815 }
816 return $flexForms;
817 }
818
819 /**
820 * Returns an array with explicit Allow/Deny fields.
821 * Used for listing these field/value pairs in be_groups forms
822 *
823 * @return array Array with information from all of $GLOBALS['TCA']
824 */
825 protected function getExplicitAuthFieldValues()
826 {
827 $languageService = static::getLanguageService();
828 $adLabel = [
829 'ALLOW' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.allow'),
830 'DENY' => $languageService->sL('LLL:EXT:lang/locallang_core.xlf:labels.deny')
831 ];
832 $allowDenyOptions = [];
833 foreach ($GLOBALS['TCA'] as $table => $_) {
834 // All field names configured:
835 if (is_array($GLOBALS['TCA'][$table]['columns'])) {
836 foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $__) {
837 $fieldConfig = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
838 if ($fieldConfig['type'] === 'select' && $fieldConfig['authMode']) {
839 // Check for items
840 if (is_array($fieldConfig['items'])) {
841 // Get Human Readable names of fields and table:
842 $allowDenyOptions[$table . ':' . $field]['tableFieldLabel'] =
843 $languageService->sL($GLOBALS['TCA'][$table]['ctrl']['title']) . ': '
844 . $languageService->sL($GLOBALS['TCA'][$table]['columns'][$field]['label']);
845 foreach ($fieldConfig['items'] as $iVal) {
846 // Values '' is not controlled by this setting.
847 if ((string)$iVal[1] !== '') {
848 // Find iMode
849 $iMode = '';
850 switch ((string)$fieldConfig['authMode']) {
851 case 'explicitAllow':
852 $iMode = 'ALLOW';
853 break;
854 case 'explicitDeny':
855 $iMode = 'DENY';
856 break;
857 case 'individual':
858 if ($iVal[4] === 'EXPL_ALLOW') {
859 $iMode = 'ALLOW';
860 } elseif ($iVal[4] === 'EXPL_DENY') {
861 $iMode = 'DENY';
862 }
863 break;
864 }
865 // Set iMode
866 if ($iMode) {
867 $allowDenyOptions[$table . ':' . $field]['items'][$iVal[1]] = [
868 $iMode,
869 $languageService->sL($iVal[0]),
870 $adLabel[$iMode]
871 ];
872 }
873 }
874 }
875 }
876 }
877 }
878 }
879 }
880 return $allowDenyOptions;
881 }
882
883 /**
884 * Build query to fetch foreign records
885 *
886 * @param array $result Result array
887 * @param string $localFieldName Current handle field name
888 * @return array Query array ready to be executed via Database->exec_SELECT_queryArray()
889 * @throws \UnexpectedValueException
890 */
891 protected function buildForeignTableQuery(array $result, $localFieldName)
892 {
893 $backendUser = $this->getBackendUser();
894
895 $foreignTableName = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table'];
896
897 if (!is_array($GLOBALS['TCA'][$foreignTableName])) {
898 throw new \UnexpectedValueException(
899 'Field ' . $localFieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
900 . $foreignTableName . ', but this table is not defined in TCA',
901 1439569743
902 );
903 }
904
905 $foreignTableClauseArray = $this->processForeignTableClause($result, $foreignTableName, $localFieldName);
906
907 $queryArray = [];
908 $queryArray['SELECT'] = BackendUtility::getCommonSelectFields($foreignTableName, $foreignTableName . '.');
909
910 // rootLevel = -1 means that elements can be on the rootlevel OR on any page (pid!=-1)
911 // rootLevel = 0 means that elements are not allowed on root level
912 // rootLevel = 1 means that elements are only on the root level (pid=0)
913 $rootLevel = 0;
914 if (isset($GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'])) {
915 $rootLevel = $GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'];
916 }
917 $deleteClause = BackendUtility::deleteClause($foreignTableName);
918 if ($rootLevel == 1 || $rootLevel == -1) {
919 $pidWhere = $foreignTableName . '.pid' . (($rootLevel == -1) ? '<>-1' : '=0');
920 $queryArray['FROM'] = $foreignTableName;
921 $queryArray['WHERE'] = $pidWhere . $deleteClause . $foreignTableClauseArray['WHERE'];
922 } else {
923 $pageClause = $backendUser->getPagePermsClause(1);
924 if ($foreignTableName === 'pages') {
925 $queryArray['FROM'] = 'pages';
926 $queryArray['WHERE'] = '1=1' . $deleteClause . ' AND' . $pageClause . $foreignTableClauseArray['WHERE'];
927 } else {
928 $queryArray['FROM'] = $foreignTableName . ', pages';
929 $queryArray['WHERE'] = 'pages.uid=' . $foreignTableName . '.pid AND pages.deleted=0'
930 . $deleteClause . ' AND' . $pageClause . $foreignTableClauseArray['WHERE'];
931 }
932 }
933
934 $queryArray['GROUPBY'] = $foreignTableClauseArray['GROUPBY'];
935 $queryArray['ORDERBY'] = $foreignTableClauseArray['ORDERBY'];
936 $queryArray['LIMIT'] = $foreignTableClauseArray['LIMIT'];
937
938 return $queryArray;
939 }
940
941 /**
942 * Replace markers in a where clause from TCA foreign_table_where
943 *
944 * ###REC_FIELD_[field name]###
945 * ###THIS_UID### - is current element uid (zero if new).
946 * ###CURRENT_PID### - is the current page id (pid of the record).
947 * ###SITEROOT###
948 * ###PAGE_TSCONFIG_ID### - a value you can set from Page TSconfig dynamically.
949 * ###PAGE_TSCONFIG_IDLIST### - a value you can set from Page TSconfig dynamically.
950 * ###PAGE_TSCONFIG_STR### - a value you can set from Page TSconfig dynamically.
951 *
952 * @param array $result Result array
953 * @param string $foreignTableName Name of foreign table
954 * @param string $localFieldName Current handle field name
955 * @return array Query parts with keys WHERE, ORDERBY, GROUPBY, LIMIT
956 */
957 protected function processForeignTableClause(array $result, $foreignTableName, $localFieldName)
958 {
959 $database = $this->getDatabaseConnection();
960 $localTable = $result['tableName'];
961
962 $foreignTableClause = '';
963 if (!empty($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
964 && is_string($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
965 ) {
966 $foreignTableClause = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'];
967 // Replace possible markers in query
968 if (strstr($foreignTableClause, '###REC_FIELD_')) {
969 // " AND table.field='###REC_FIELD_field1###' AND ..." -> array(" AND table.field='", "field1###' AND ...")
970 $whereClauseParts = explode('###REC_FIELD_', $foreignTableClause);
971 foreach ($whereClauseParts as $key => $value) {
972 if ($key !== 0) {
973 // "field1###' AND ..." -> array("field1", "' AND ...")
974 $whereClauseSubParts = explode('###', $value, 2);
975 // @todo: Throw exception if there is no value? What happens for NEW records?
976 $databaseRowKey = empty($result['flexParentDatabaseRow']) ? 'databaseRow' : 'flexParentDatabaseRow';
977 $rowFieldValue = isset($result[$databaseRowKey][$whereClauseSubParts[0]]) ? $result[$databaseRowKey][$whereClauseSubParts[0]] : '';
978 if (is_array($rowFieldValue)) {
979 // If a select or group field is used here, it may have been processed already and
980 // is now an array. Use first selected value in this case.
981 $rowFieldValue = $rowFieldValue[0];
982 }
983 if (substr($whereClauseParts[0], -1) === '\'' && $whereClauseSubParts[1][0] === '\'') {
984 $whereClauseParts[$key] = $database->quoteStr($rowFieldValue, $foreignTableName) . $whereClauseSubParts[1];
985 } else {
986 $whereClauseParts[$key] = $database->fullQuoteStr($rowFieldValue, $foreignTableName) . $whereClauseSubParts[1];
987 }
988 }
989 }
990 $foreignTableClause = implode('', $whereClauseParts);
991 }
992
993 $siteRootUid = 0;
994 foreach ($result['rootline'] as $rootlinePage) {
995 if (!empty($rootlinePage['is_siteroot'])) {
996 $siteRootUid = (int)$rootlinePage['uid'];
997 break;
998 }
999 }
1000 $pageTsConfigId = 0;
1001 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID']) {
1002 $pageTsConfigId = (int)$result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID'];
1003 }
1004 $pageTsConfigIdList = 0;
1005 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST']) {
1006 $pageTsConfigIdList = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST'];
1007 $pageTsConfigIdListArray = GeneralUtility::trimExplode(',', $pageTsConfigIdList, true);
1008 $pageTsConfigIdList = [];
1009 foreach ($pageTsConfigIdListArray as $pageTsConfigIdListElement) {
1010 if (MathUtility::canBeInterpretedAsInteger($pageTsConfigIdListElement)) {
1011 $pageTsConfigIdList[] = (int)$pageTsConfigIdListElement;
1012 }
1013 }
1014 $pageTsConfigIdList = implode(',', $pageTsConfigIdList);
1015 }
1016 $pageTsConfigString = '';
1017 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR']) {
1018 $pageTsConfigString = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR'];
1019 $pageTsConfigString = $database->quoteStr($pageTsConfigString, $foreignTableName);
1020 }
1021
1022 $foreignTableClause = str_replace(
1023 [
1024 '###CURRENT_PID###',
1025 '###THIS_UID###',
1026 '###SITEROOT###',
1027 '###PAGE_TSCONFIG_ID###',
1028 '###PAGE_TSCONFIG_IDLIST###',
1029 '###PAGE_TSCONFIG_STR###'
1030 ],
1031 [
1032 (int)$result['effectivePid'],
1033 (int)$result['databaseRow']['uid'],
1034 $siteRootUid,
1035 $pageTsConfigId,
1036 $pageTsConfigIdList,
1037 $pageTsConfigString
1038 ],
1039 $foreignTableClause
1040 );
1041 }
1042
1043 // Split the clause into an array with keys WHERE, GROUPBY, ORDERBY, LIMIT
1044 // Prepend a space to make sure "[[:space:]]+" will find a space there for the first element.
1045 $foreignTableClause = ' ' . $foreignTableClause;
1046 $foreignTableClauseArray = [
1047 'WHERE' => '',
1048 'GROUPBY' => '',
1049 'ORDERBY' => '',
1050 'LIMIT' => '',
1051 ];
1052 // Find LIMIT
1053 $reg = [];
1054 if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1055 $foreignTableClauseArray['LIMIT'] = trim($reg[2]);
1056 $foreignTableClause = $reg[1];
1057 }
1058 // Find ORDER BY
1059 $reg = [];
1060 if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1061 $foreignTableClauseArray['ORDERBY'] = trim($reg[2]);
1062 $foreignTableClause = $reg[1];
1063 }
1064 // Find GROUP BY
1065 $reg = [];
1066 if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1067 $foreignTableClauseArray['GROUPBY'] = trim($reg[2]);
1068 $foreignTableClause = $reg[1];
1069 }
1070 // Rest is assumed to be "WHERE" clause
1071 $foreignTableClauseArray['WHERE'] = $foreignTableClause;
1072
1073 return $foreignTableClauseArray;
1074 }
1075
1076 /**
1077 * Convert the current database values into an array
1078 *
1079 * @param array $row database row
1080 * @param string $fieldName fieldname to process
1081 * @return array
1082 */
1083 protected function processDatabaseFieldValue(array $row, $fieldName)
1084 {
1085 $currentDatabaseValues = array_key_exists($fieldName, $row)
1086 ? $row[$fieldName]
1087 : '';
1088 return GeneralUtility::trimExplode(',', $currentDatabaseValues, true);
1089 }
1090
1091 /**
1092 * Validate and sanitize database row values of the select field with the given name.
1093 * Creates an array out of databaseRow[selectField] values.
1094 *
1095 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1096 *
1097 * @param array $result The current result array.
1098 * @param string $fieldName Name of the current select field.
1099 * @param array $staticValues Array with statically defined items, item value is used as array key.
1100 * @return array
1101 */
1102 protected function processSelectFieldValue(array $result, $fieldName, array $staticValues)
1103 {
1104 $fieldConfig = $result['processedTca']['columns'][$fieldName];
1105
1106 $currentDatabaseValueArray = array_key_exists($fieldName, $result['databaseRow']) ? $result['databaseRow'][$fieldName] : [];
1107 $newDatabaseValueArray = [];
1108
1109 // Add all values that were defined by static methods and do not come from the relation
1110 // e.g. TCA, TSconfig, itemProcFunc etc.
1111 foreach ($currentDatabaseValueArray as $value) {
1112 if (isset($staticValues[$value])) {
1113 $newDatabaseValueArray[] = $value;
1114 }
1115 }
1116
1117 if (isset($fieldConfig['config']['foreign_table']) && !empty($fieldConfig['config']['foreign_table'])) {
1118 /** @var RelationHandler $relationHandler */
1119 $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
1120 $relationHandler->registerNonTableValues = !empty($fieldConfig['config']['allowNonIdValues']);
1121 if (!empty($fieldConfig['config']['MM']) && $result['command'] !== 'new') {
1122 // MM relation
1123 $relationHandler->start(
1124 implode(',', $currentDatabaseValueArray),
1125 $fieldConfig['config']['foreign_table'],
1126 $fieldConfig['config']['MM'],
1127 $result['databaseRow']['uid'],
1128 $result['tableName'],
1129 $fieldConfig['config']
1130 );
1131 } else {
1132 // Non MM relation
1133 // If not dealing with MM relations, use default live uid, not versioned uid for record relations
1134 $relationHandler->start(
1135 implode(',', $currentDatabaseValueArray),
1136 $fieldConfig['config']['foreign_table'],
1137 '',
1138 $this->getLiveUid($result),
1139 $result['tableName'],
1140 $fieldConfig['config']
1141 );
1142 }
1143 $newDatabaseValueArray = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
1144 }
1145
1146 return array_unique($newDatabaseValueArray);
1147 }
1148
1149 /**
1150 * Translate the item labels
1151 *
1152 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1153 *
1154 * @param array $result Result array
1155 * @param array $itemArray Items
1156 * @param string $table
1157 * @param string $fieldName
1158 * @return array
1159 */
1160 public function translateLabels(array $result, array $itemArray, $table, $fieldName)
1161 {
1162 $languageService = $this->getLanguageService();
1163
1164 foreach ($itemArray as $key => $item) {
1165 if (!isset($dynamicItems[$key])) {
1166 $staticValues[$item[1]] = $item;
1167 }
1168 if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1169 && !empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1170 ) {
1171 $label = $languageService->sL($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]]);
1172 } else {
1173 $label = $languageService->sL(trim($item[0]));
1174 }
1175 $value = strlen((string)$item[1]) > 0 ? $item[1] : '';
1176 $icon = $item[2] ?: null;
1177 $helpText = $item[3] ?: null;
1178 $itemArray[$key] = [
1179 $label,
1180 $value,
1181 $icon,
1182 $helpText
1183 ];
1184 }
1185
1186 return $itemArray;
1187 }
1188
1189 /**
1190 * Sanitize incoming item array
1191 *
1192 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1193 *
1194 * @param mixed $itemArray
1195 * @param string $tableName
1196 * @param string $fieldName
1197 * @throws \UnexpectedValueException
1198 * @return array
1199 */
1200 public function sanitizeItemArray($itemArray, $tableName, $fieldName)
1201 {
1202 if (!is_array($itemArray)) {
1203 $itemArray = [];
1204 }
1205 foreach ($itemArray as $item) {
1206 if (!is_array($item)) {
1207 throw new \UnexpectedValueException(
1208 'An item in field ' . $fieldName . ' of table ' . $tableName . ' is not an array as expected',
1209 1439288036
1210 );
1211 }
1212 }
1213
1214 return $itemArray;
1215 }
1216
1217 /**
1218 * Make sure maxitems is always filled with a valid integer value.
1219 *
1220 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1221 *
1222 * @param mixed $maxItems
1223 * @return int
1224 */
1225 public function sanitizeMaxItems($maxItems)
1226 {
1227 if (!empty($maxItems)
1228 && (int)$maxItems > 1
1229 ) {
1230 $maxItems = (int)$maxItems;
1231 } else {
1232 $maxItems = 1;
1233 }
1234
1235 return $maxItems;
1236 }
1237
1238 /**
1239 * Gets the record uid of the live default record. If already
1240 * pointing to the live record, the submitted record uid is returned.
1241 *
1242 * @param array $result Result array
1243 * @return int
1244 * @throws \UnexpectedValueException
1245 */
1246 protected function getLiveUid(array $result)
1247 {
1248 $table = $result['tableName'];
1249 $row = $result['databaseRow'];
1250 $uid = $row['uid'];
1251 if (!empty($result['processedTca']['ctrl']['versioningWS'])
1252 && $result['pid'] === -1
1253 ) {
1254 if (empty($row['t3ver_oid'])) {
1255 throw new \UnexpectedValueException(
1256 'No t3ver_oid found for record ' . $row['uid'] . ' on table ' . $table,
1257 1440066481
1258 );
1259 }
1260 $uid = $row['t3ver_oid'];
1261 }
1262 return $uid;
1263 }
1264
1265 /**
1266 * Determine the static values in the item array
1267 *
1268 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1269 *
1270 * @param array $itemArray All item records for the select field
1271 * @param array $dynamicItemArray Item records from dynamic sources
1272 * @return array
1273 */
1274 public function getStaticValues($itemArray, $dynamicItemArray)
1275 {
1276 $staticValues = [];
1277 foreach ($itemArray as $key => $item) {
1278 if (!isset($dynamicItemArray[$key])) {
1279 $staticValues[$item[1]] = $item;
1280 }
1281 }
1282 return $staticValues;
1283 }
1284
1285 /**
1286 * @return LanguageService
1287 */
1288 protected function getLanguageService()
1289 {
1290 return $GLOBALS['LANG'];
1291 }
1292
1293 /**
1294 * @return DatabaseConnection
1295 */
1296 protected function getDatabaseConnection()
1297 {
1298 return $GLOBALS['TYPO3_DB'];
1299 }
1300
1301 /**
1302 * @return BackendUserAuthentication
1303 */
1304 protected function getBackendUser()
1305 {
1306 return $GLOBALS['BE_USER'];
1307 }
1308 }