[TASK] Remove ext:dbal from installation steps
[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\ConnectionPool;
21 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
22 use TYPO3\CMS\Core\Database\Query\QueryHelper;
23 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
24 use TYPO3\CMS\Core\Database\RelationHandler;
25 use TYPO3\CMS\Core\Imaging\IconFactory;
26 use TYPO3\CMS\Core\Messaging\FlashMessage;
27 use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
28 use TYPO3\CMS\Core\Messaging\FlashMessageService;
29 use TYPO3\CMS\Core\Utility\ArrayUtility;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32 use TYPO3\CMS\Lang\LanguageService;
33
34 /**
35 * Contains methods used by Data providers that handle elements
36 * with single items like select, radio and some more.
37 */
38 abstract class AbstractItemProvider
39 {
40 /**
41 * Resolve "itemProcFunc" of elements.
42 *
43 * @param array $result Main result array
44 * @param string $fieldName Field name to handle item list for
45 * @param array $items Existing items array
46 * @return array New list of item elements
47 */
48 protected function resolveItemProcessorFunction(array $result, $fieldName, array $items)
49 {
50 $table = $result['tableName'];
51 $config = $result['processedTca']['columns'][$fieldName]['config'];
52
53 $pageTsProcessorParameters = null;
54 if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'])) {
55 $pageTsProcessorParameters = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'];
56 }
57 $processorParameters = [
58 // Function manipulates $items directly and return nothing
59 'items' => &$items,
60 'config' => $config,
61 'TSconfig' => $pageTsProcessorParameters,
62 'table' => $table,
63 'row' => $result['databaseRow'],
64 'field' => $fieldName,
65 ];
66 if (!empty($result['flexParentDatabaseRow'])) {
67 $processorParameters['flexParentDatabaseRow'] = $result['flexParentDatabaseRow'];
68 }
69
70 try {
71 GeneralUtility::callUserFunction($config['itemsProcFunc'], $processorParameters, $this);
72 } catch (\Exception $exception) {
73 // The itemsProcFunc method may throw an exception, create a flash message if so
74 $languageService = $this->getLanguageService();
75 $fieldLabel = $fieldName;
76 if (!empty($result['processedTca']['columns'][$fieldName]['label'])) {
77 $fieldLabel = $languageService->sL($result['processedTca']['columns'][$fieldName]['label']);
78 }
79 $message = sprintf(
80 $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.items_proc_func_error'),
81 $fieldLabel,
82 $exception->getMessage()
83 );
84 /** @var FlashMessage $flashMessage */
85 $flashMessage = GeneralUtility::makeInstance(
86 FlashMessage::class,
87 $message,
88 '',
89 FlashMessage::ERROR,
90 true
91 );
92 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
93 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
94 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
95 $defaultFlashMessageQueue->enqueue($flashMessage);
96 }
97
98 return $items;
99 }
100
101 /**
102 * PageTsConfig addItems:
103 *
104 * TCEFORMS.aTable.aField[.types][.aType].addItems.aValue = aLabel,
105 * with type specific options merged by pageTsConfig already
106 *
107 * Used by TcaSelectItems and TcaSelectTreeItems data providers
108 *
109 * @param array $result result array
110 * @param string $fieldName Current handle field name
111 * @param array $items Incoming items
112 * @return array Modified item array
113 */
114 protected function addItemsFromPageTsConfig(array $result, $fieldName, array $items)
115 {
116 $table = $result['tableName'];
117 if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
118 && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
119 ) {
120 $addItemsArray = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'];
121 foreach ($addItemsArray as $value => $label) {
122 // If the value ends with a dot, it is a subelement like "34.icon = mylabel.png", skip it
123 if (substr($value, -1) === '.') {
124 continue;
125 }
126 // Check if value "34 = mylabel" also has a "34.icon = myImage.png"
127 $icon = null;
128 if (isset($addItemsArray[$value . '.'])
129 && is_array($addItemsArray[$value . '.'])
130 && !empty($addItemsArray[$value . '.']['icon'])
131 ) {
132 $icon = $addItemsArray[$value . '.']['icon'];
133 }
134 $items[] = [$label, $value, $icon];
135 }
136 }
137 return $items;
138 }
139
140 /**
141 * TCA config "special" evaluation. Add them to $items
142 *
143 * Used by TcaSelectItems and TcaSelectTreeItems data providers
144 *
145 * @param array $result Result array
146 * @param string $fieldName Current handle field name
147 * @param array $items Incoming items
148 * @return array Modified item array
149 * @throws \UnexpectedValueException
150 */
151 protected function addItemsFromSpecial(array $result, $fieldName, array $items)
152 {
153 // Guard
154 if (empty($result['processedTca']['columns'][$fieldName]['config']['special'])
155 || !is_string($result['processedTca']['columns'][$fieldName]['config']['special'])
156 ) {
157 return $items;
158 }
159
160 $languageService = $this->getLanguageService();
161 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
162
163 $special = $result['processedTca']['columns'][$fieldName]['config']['special'];
164 switch (true) {
165 case ($special === 'tables'):
166 foreach ($GLOBALS['TCA'] as $currentTable => $_) {
167 if (!empty($GLOBALS['TCA'][$currentTable]['ctrl']['adminOnly'])) {
168 // Hide "admin only" tables
169 continue;
170 }
171 $label = !empty($GLOBALS['TCA'][$currentTable]['ctrl']['title']) ? $GLOBALS['TCA'][$currentTable]['ctrl']['title'] : '';
172 $icon = $iconFactory->mapRecordTypeToIconIdentifier($currentTable, []);
173 $helpText = [];
174 $languageService->loadSingleTableDescription($currentTable);
175 // @todo: check if this actually works, currently help texts are missing
176 $helpTextArray = $GLOBALS['TCA_DESCR'][$currentTable]['columns'][''];
177 if (!empty($helpTextArray['description'])) {
178 $helpText['description'] = $helpTextArray['description'];
179 }
180 $items[] = [$label, $currentTable, $icon, $helpText];
181 }
182 break;
183 case ($special === 'pagetypes'):
184 if (isset($GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'])
185 && is_array($GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'])
186 ) {
187 $specialItems = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'];
188 foreach ($specialItems as $specialItem) {
189 if (!is_array($specialItem) || $specialItem[1] === '--div--') {
190 // Skip non arrays and divider items
191 continue;
192 }
193 $label = $specialItem[0];
194 $value = $specialItem[1];
195 $icon = $iconFactory->mapRecordTypeToIconIdentifier('pages', ['doktype' => $specialItem[1]]);
196 $items[] = [$label, $value, $icon];
197 }
198 }
199 break;
200 case ($special === 'exclude'):
201 $excludeArrays = $this->getExcludeFields();
202 foreach ($excludeArrays as $excludeArray) {
203 // If the field comes from a FlexForm, the syntax is more complex
204 if ($excludeArray['origin'] === 'flexForm') {
205 // The field comes from a plugins FlexForm
206 // Add header if not yet set for plugin section
207 if (!isset($items[$excludeArray['sectionHeader']])) {
208 // there is no icon handling for plugins - we take the icon from the table
209 $icon = $iconFactory->mapRecordTypeToIconIdentifier($excludeArray['table'], []);
210 $items[$excludeArray['sectionHeader']] = [
211 $excludeArray['sectionHeader'],
212 '--div--',
213 $icon
214 ];
215 }
216 } else {
217 // Add header if not yet set for table
218 if (!isset($items[$excludeArray['table']])) {
219 $icon = $iconFactory->mapRecordTypeToIconIdentifier($excludeArray['table'], []);
220 $items[$excludeArray['table']] = [
221 $GLOBALS['TCA'][$excludeArray['table']]['ctrl']['title'],
222 '--div--',
223 $icon
224 ];
225 }
226 }
227 // Add help text
228 $helpText = [];
229 $languageService->loadSingleTableDescription($excludeArray['table']);
230 $helpTextArray = $GLOBALS['TCA_DESCR'][$excludeArray['table']]['columns'][$excludeArray['table']];
231 if (!empty($helpTextArray['description'])) {
232 $helpText['description'] = $helpTextArray['description'];
233 }
234 // Item configuration:
235 $items[] = [
236 rtrim($excludeArray['origin'] === 'flexForm' ? $excludeArray['fieldLabel'] : $languageService->sL($GLOBALS['TCA'][$excludeArray['table']]['columns'][$excludeArray['fieldName']]['label']), ':') . ' (' . $excludeArray['fieldName'] . ')',
237 $excludeArray['table'] . ':' . $excludeArray['fullField'] ,
238 'empty-empty',
239 $helpText
240 ];
241 }
242 break;
243 case ($special === 'explicitValues'):
244 $theTypes = $this->getExplicitAuthFieldValues();
245 $icons = [
246 'ALLOW' => 'status-status-permission-granted',
247 'DENY' => 'status-status-permission-denied'
248 ];
249 // Traverse types:
250 foreach ($theTypes as $tableFieldKey => $theTypeArrays) {
251 if (is_array($theTypeArrays['items'])) {
252 // Add header:
253 $items[] = [
254 $theTypeArrays['tableFieldLabel'],
255 '--div--',
256 ];
257 // Traverse options for this field:
258 foreach ($theTypeArrays['items'] as $itemValue => $itemContent) {
259 // Add item to be selected:
260 $items[] = [
261 '[' . $itemContent[2] . '] ' . $itemContent[1],
262 $tableFieldKey . ':' . preg_replace('/[:|,]/', '', $itemValue) . ':' . $itemContent[0],
263 $icons[$itemContent[0]]
264 ];
265 }
266 }
267 }
268 break;
269 case ($special === 'languages'):
270 foreach ($result['systemLanguageRows'] as $language) {
271 if ($language['uid'] !== -1) {
272 $items[] = [
273 0 => $language['title'] . ' [' . $language['uid'] . ']',
274 1 => $language['uid'],
275 2 => $language['flagIconIdentifier']
276 ];
277 }
278 }
279 break;
280 case ($special === 'custom'):
281 $customOptions = $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'];
282 if (is_array($customOptions)) {
283 foreach ($customOptions as $coKey => $coValue) {
284 if (is_array($coValue['items'])) {
285 // Add header:
286 $items[] = [
287 $languageService->sL($coValue['header']),
288 '--div--'
289 ];
290 // Traverse items:
291 foreach ($coValue['items'] as $itemKey => $itemCfg) {
292 $icon = 'empty-empty';
293 $helpText = [];
294 if (!empty($itemCfg[2])) {
295 $helpText['description'] = $languageService->sL($itemCfg[2]);
296 }
297 $items[] = [
298 $languageService->sL($itemCfg[0]),
299 $coKey . ':' . preg_replace('/[:|,]/', '', $itemKey),
300 $icon,
301 $helpText
302 ];
303 }
304 }
305 }
306 }
307 break;
308 case ($special === 'modListGroup' || $special === 'modListUser'):
309 /** @var ModuleLoader $loadModules */
310 $loadModules = GeneralUtility::makeInstance(ModuleLoader::class);
311 $loadModules->load($GLOBALS['TBE_MODULES']);
312 $modList = $special === 'modListUser' ? $loadModules->modListUser : $loadModules->modListGroup;
313 if (is_array($modList)) {
314 foreach ($modList as $theMod) {
315 $moduleLabels = $loadModules->getLabelsForModule($theMod);
316 list($mainModule, $subModule) = explode('_', $theMod, 2);
317 // Icon:
318 if (!empty($subModule)) {
319 $icon = $loadModules->modules[$mainModule]['sub'][$subModule]['iconIdentifier'];
320 } else {
321 $icon = $loadModules->modules[$theMod]['iconIdentifier'];
322 }
323 // Add help text
324 $helpText = [
325 'title' => $languageService->sL($moduleLabels['shortdescription']),
326 'description' => $languageService->sL($moduleLabels['description'])
327 ];
328
329 $label = '';
330 // Add label for main module if this is a submodule
331 if (!empty($subModule)) {
332 $mainModuleLabels = $loadModules->getLabelsForModule($mainModule);
333 $label .= $languageService->sL($mainModuleLabels['title']) . '>';
334 }
335 // Add modules own label now
336 $label .= $languageService->sL($moduleLabels['title']);
337
338 // Item configuration
339 $items[] = [$label, $theMod, $icon, $helpText];
340 }
341 }
342 break;
343 default:
344 throw new \UnexpectedValueException(
345 'Unknown special value ' . $special . ' for field ' . $fieldName . ' of table ' . $result['tableName'],
346 1439298496
347 );
348 }
349 return $items;
350 }
351
352 /**
353 * TCA config "fileFolder" evaluation. Add them to $items
354 *
355 * Used by TcaSelectItems and TcaSelectTreeItems data providers
356 *
357 * @param array $result Result array
358 * @param string $fieldName Current handle field name
359 * @param array $items Incoming items
360 * @return array Modified item array
361 */
362 protected function addItemsFromFolder(array $result, $fieldName, array $items)
363 {
364 if (empty($result['processedTca']['columns'][$fieldName]['config']['fileFolder'])
365 || !is_string($result['processedTca']['columns'][$fieldName]['config']['fileFolder'])
366 ) {
367 return $items;
368 }
369
370 $fileFolder = $result['processedTca']['columns'][$fieldName]['config']['fileFolder'];
371 $fileFolder = GeneralUtility::getFileAbsFileName($fileFolder);
372 $fileFolder = rtrim($fileFolder, '/') . '/';
373
374 if (@is_dir($fileFolder)) {
375 $fileExtensionList = '';
376 if (!empty($result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'])
377 && is_string($result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'])
378 ) {
379 $fileExtensionList = $result['processedTca']['columns'][$fieldName]['config']['fileFolder_extList'];
380 }
381 $recursionLevels = isset($fieldValue['config']['fileFolder_recursions'])
382 ? MathUtility::forceIntegerInRange($fieldValue['config']['fileFolder_recursions'], 0, 99)
383 : 99;
384 $fileArray = GeneralUtility::getAllFilesAndFoldersInPath([], $fileFolder, $fileExtensionList, 0, $recursionLevels);
385 $fileArray = GeneralUtility::removePrefixPathFromList($fileArray, $fileFolder);
386 foreach ($fileArray as $fileReference) {
387 $fileInformation = pathinfo($fileReference);
388 $icon = GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($fileInformation['extension']))
389 ? $fileFolder . $fileReference
390 : '';
391 $items[] = [
392 $fileReference,
393 $fileReference,
394 $icon
395 ];
396 }
397 }
398
399 return $items;
400 }
401
402 /**
403 * TCA config "foreign_table" evaluation. Add them to $items
404 *
405 * Used by TcaSelectItems and TcaSelectTreeItems data providers
406 *
407 * @param array $result Result array
408 * @param string $fieldName Current handle field name
409 * @param array $items Incoming items
410 * @return array Modified item array
411 * @throws \UnexpectedValueException
412 */
413 protected function addItemsFromForeignTable(array $result, $fieldName, array $items)
414 {
415 // Guard
416 if (empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
417 || !is_string($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
418 ) {
419 return $items;
420 }
421
422 $languageService = $this->getLanguageService();
423
424 $foreignTable = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
425
426 if (!is_array($GLOBALS['TCA'][$foreignTable])) {
427 throw new \UnexpectedValueException(
428 'Field ' . $fieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
429 . $foreignTable . ', but this table is not defined in TCA',
430 1439569743
431 );
432 }
433
434 $queryBuilder = $this->buildForeignTableQueryBuilder($result, $fieldName);
435 $queryResult = $queryBuilder->execute();
436
437 // Early return on error with flash message
438 $databaseError = $queryResult->errorInfo();
439 if (!empty($databaseError)) {
440 $msg = $databaseError . '. ';
441 $msg .= $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch');
442 $msgTitle = $languageService->sL('LLL:EXT:lang/locallang_core.xlf:error.database_schema_mismatch_title');
443 /** @var $flashMessage FlashMessage */
444 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $msg, $msgTitle, FlashMessage::ERROR, true);
445 /** @var $flashMessageService FlashMessageService */
446 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
447 /** @var $defaultFlashMessageQueue FlashMessageQueue */
448 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
449 $defaultFlashMessageQueue->enqueue($flashMessage);
450 $queryResult->closeCursor();
451 return $items;
452 }
453
454 $labelPrefix = '';
455 if (!empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'])) {
456 $labelPrefix = $result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'];
457 $labelPrefix = $languageService->sL($labelPrefix);
458 }
459
460 $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
461
462 while ($foreignRow = $queryResult->fetch()) {
463 BackendUtility::workspaceOL($foreignTable, $foreignRow);
464 if (is_array($foreignRow)) {
465 // If the foreign table sets selicon_field, this field can contain an image
466 // that represents this specific row.
467 $iconFieldName = '';
468 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'])) {
469 $iconFieldName = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'];
470 }
471 $iconPath = '';
472 if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'])) {
473 $iconPath = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field_path'];
474 }
475 if ($iconFieldName && $iconPath && $foreignRow[$iconFieldName]) {
476 // Prepare the row icon if available
477 $iParts = GeneralUtility::trimExplode(',', $foreignRow[$iconFieldName], true);
478 $icon = $iconPath . '/' . trim($iParts[0]);
479 } else {
480 // Else, determine icon based on record type, or a generic fallback
481 $icon = $iconFactory->mapRecordTypeToIconIdentifier($foreignTable, $foreignRow);
482 }
483 // Add the item
484 $items[] = [
485 $labelPrefix . BackendUtility::getRecordTitle($foreignTable, $foreignRow),
486 $foreignRow['uid'],
487 $icon
488 ];
489 }
490 }
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 QueryBuilder
903 */
904 protected function buildForeignTableQueryBuilder(array $result, string $localFieldName): QueryBuilder
905 {
906 $backendUser = $this->getBackendUser();
907
908 $foreignTableName = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table'];
909 $foreignTableClauseArray = $this->processForeignTableClause($result, $foreignTableName, $localFieldName);
910
911 $fieldList = BackendUtility::getCommonSelectFields($foreignTableName, $foreignTableName . '.');
912 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
913 ->getQueryBuilderForTable($foreignTableName);
914
915 $queryBuilder->getRestrictions()
916 ->removeAll()
917 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
918
919 $queryBuilder
920 ->select(...GeneralUtility::trimExplode(',', $fieldList, true))
921 ->from($foreignTableName)
922 ->where($foreignTableClauseArray['WHERE']);
923
924 if (!empty($foreignTableClauseArray['GROUPBY'])) {
925 $queryBuilder->groupBy($foreignTableClauseArray['GROUPBY']);
926 }
927
928 if (!empty($foreignTableClauseArray['ORDERBY'])) {
929 foreach ($foreignTableClauseArray['ORDERBY'] as $orderPair) {
930 list($fieldName, $order) = $orderPair;
931 $queryBuilder->addOrderBy($fieldName, $order);
932 }
933 }
934
935 if (!empty($foreignTableClauseArray['LIMIT'])) {
936 if (!empty($foreignTableClauseArray['LIMIT'][1])) {
937 $queryBuilder->setMaxResults($foreignTableClauseArray['LIMIT'][1]);
938 $queryBuilder->setFirstResult($foreignTableClauseArray['LIMIT'][0]);
939 } elseif (!empty($foreignTableClauseArray['LIMIT'][0])) {
940 $queryBuilder->setMaxResults($foreignTableClauseArray['LIMIT'][0]);
941 }
942 }
943
944 // rootLevel = -1 means that elements can be on the rootlevel OR on any page (pid!=-1)
945 // rootLevel = 0 means that elements are not allowed on root level
946 // rootLevel = 1 means that elements are only on the root level (pid=0)
947 $rootLevel = 0;
948 if (isset($GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'])) {
949 $rootLevel = (int)$GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'];
950 }
951
952 if ($rootLevel === -1) {
953 $queryBuilder->andWhere($queryBuilder->expr()->neq($foreignTableName . '.pid', -1));
954 } elseif ($rootLevel === 1) {
955 $queryBuilder->andWhere($queryBuilder->expr()->eq($foreignTableName . '.pid', 0));
956 } else {
957 $queryBuilder->andWhere($backendUser->getPagePermsClause(1));
958 if ($foreignTableName !== 'pages') {
959 $queryBuilder
960 ->from('pages')
961 ->andWhere(
962 $queryBuilder->expr()->eq(
963 'pages.uid',
964 $queryBuilder->quoteIdentifier($foreignTableName . '.pid')
965 )
966 );
967 }
968 }
969
970 return $queryBuilder;
971 }
972
973 /**
974 * Replace markers in a where clause from TCA foreign_table_where
975 *
976 * ###REC_FIELD_[field name]###
977 * ###THIS_UID### - is current element uid (zero if new).
978 * ###CURRENT_PID### - is the current page id (pid of the record).
979 * ###SITEROOT###
980 * ###PAGE_TSCONFIG_ID### - a value you can set from Page TSconfig dynamically.
981 * ###PAGE_TSCONFIG_IDLIST### - a value you can set from Page TSconfig dynamically.
982 * ###PAGE_TSCONFIG_STR### - a value you can set from Page TSconfig dynamically.
983 *
984 * @param array $result Result array
985 * @param string $foreignTableName Name of foreign table
986 * @param string $localFieldName Current handle field name
987 * @return array Query parts with keys WHERE, ORDERBY, GROUPBY, LIMIT
988 */
989 protected function processForeignTableClause(array $result, $foreignTableName, $localFieldName)
990 {
991 $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($foreignTableName);
992 $localTable = $result['tableName'];
993 $effectivePid = $result['effectivePid'];
994
995 $foreignTableClause = '';
996 if (!empty($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
997 && is_string($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
998 ) {
999 $foreignTableClause = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'];
1000 // Replace possible markers in query
1001 if (strstr($foreignTableClause, '###REC_FIELD_')) {
1002 // " AND table.field='###REC_FIELD_field1###' AND ..." -> array(" AND table.field='", "field1###' AND ...")
1003 $whereClauseParts = explode('###REC_FIELD_', $foreignTableClause);
1004 foreach ($whereClauseParts as $key => $value) {
1005 if ($key !== 0) {
1006 // "field1###' AND ..." -> array("field1", "' AND ...")
1007 $whereClauseSubParts = explode('###', $value, 2);
1008 // @todo: Throw exception if there is no value? What happens for NEW records?
1009 $databaseRowKey = empty($result['flexParentDatabaseRow']) ? 'databaseRow' : 'flexParentDatabaseRow';
1010 $rowFieldValue = isset($result[$databaseRowKey][$whereClauseSubParts[0]]) ? $result[$databaseRowKey][$whereClauseSubParts[0]] : '';
1011 if (is_array($rowFieldValue)) {
1012 // If a select or group field is used here, it may have been processed already and
1013 // is now an array. Use first selected value in this case.
1014 $rowFieldValue = $rowFieldValue[0];
1015 }
1016 if (substr($whereClauseParts[0], -1) === '\'' && $whereClauseSubParts[1][0] === '\'') {
1017 $whereClauseParts[0] = substr($whereClauseParts[0], 0, -1);
1018 $whereClauseSubParts[1] = substr($whereClauseSubParts[1], 1);
1019 }
1020 $whereClauseParts[$key] = $connection->quote($rowFieldValue) . $whereClauseSubParts[1];
1021 }
1022 }
1023 $foreignTableClause = implode('', $whereClauseParts);
1024 }
1025 // Use pid from parent page clause if in flex flom context
1026 if (strpos($foreignTableClause, '###CURRENT_PID###') !== false
1027 && !empty($result['flexParentDatabaseRow']['pid'])
1028 ) {
1029 $effectivePid = $result['flexParentDatabaseRow']['pid'];
1030 }
1031
1032 $siteRootUid = 0;
1033 foreach ($result['rootline'] as $rootlinePage) {
1034 if (!empty($rootlinePage['is_siteroot'])) {
1035 $siteRootUid = (int)$rootlinePage['uid'];
1036 break;
1037 }
1038 }
1039
1040 $pageTsConfigId = 0;
1041 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_ID']) {
1042 // @deprecated since TYPO3 v8, will be removed in TYPO3 v9 - see also the flexHack part in TcaFlexProcess
1043 $pageTsConfigId = (int)$result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_ID'];
1044 }
1045 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID']) {
1046 $pageTsConfigId = (int)$result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID'];
1047 }
1048
1049 $pageTsConfigIdList = 0;
1050 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_IDLIST']) {
1051 // @deprecated since TYPO3 v8, will be removed in TYPO3 v9 - see also the flexHack part in TcaFlexProcess
1052 $pageTsConfigIdList = $result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_IDLIST'];
1053 }
1054 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST']) {
1055 $pageTsConfigIdList = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST'];
1056 }
1057 $pageTsConfigIdListArray = GeneralUtility::trimExplode(',', $pageTsConfigIdList, true);
1058 $pageTsConfigIdList = [];
1059 foreach ($pageTsConfigIdListArray as $pageTsConfigIdListElement) {
1060 if (MathUtility::canBeInterpretedAsInteger($pageTsConfigIdListElement)) {
1061 $pageTsConfigIdList[] = (int)$pageTsConfigIdListElement;
1062 }
1063 }
1064 $pageTsConfigIdList = implode(',', $pageTsConfigIdList);
1065
1066 $pageTsConfigString = '';
1067 if ($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_STR']) {
1068 // @deprecated since TYPO3 v8, will be removed in TYPO3 v9 - see also the flexHack part in TcaFlexProcess
1069 $pageTsConfigString = $connection->quote($result['pageTsConfig']['flexHack.']['PAGE_TSCONFIG_STR']);
1070 }
1071 if ($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR']) {
1072 $pageTsConfigString = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR'];
1073 $pageTsConfigString = $connection->quote($pageTsConfigString);
1074 }
1075
1076 $foreignTableClause = str_replace(
1077 [
1078 '###CURRENT_PID###',
1079 '###THIS_UID###',
1080 '###SITEROOT###',
1081 '###PAGE_TSCONFIG_ID###',
1082 '###PAGE_TSCONFIG_IDLIST###',
1083 '\'###PAGE_TSCONFIG_STR###\'',
1084 '###PAGE_TSCONFIG_STR###'
1085 ],
1086 [
1087 (int)$effectivePid,
1088 (int)$result['databaseRow']['uid'],
1089 $siteRootUid,
1090 $pageTsConfigId,
1091 $pageTsConfigIdList,
1092 $pageTsConfigString,
1093 $pageTsConfigString
1094 ],
1095 $foreignTableClause
1096 );
1097 }
1098
1099 // Split the clause into an array with keys WHERE, GROUPBY, ORDERBY, LIMIT
1100 // Prepend a space to make sure "[[:space:]]+" will find a space there for the first element.
1101 $foreignTableClause = ' ' . $foreignTableClause;
1102 $foreignTableClauseArray = [
1103 'WHERE' => '',
1104 'GROUPBY' => '',
1105 'ORDERBY' => '',
1106 'LIMIT' => '',
1107 ];
1108 // Find LIMIT
1109 $reg = [];
1110 if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1111 $foreignTableClauseArray['LIMIT'] = GeneralUtility::intExplode(',', trim($reg[2]), true);
1112 $foreignTableClause = $reg[1];
1113 }
1114 // Find ORDER BY
1115 $reg = [];
1116 if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1117 $foreignTableClauseArray['ORDERBY'] = QueryHelper::parseOrderBy(trim($reg[2]));
1118 $foreignTableClause = $reg[1];
1119 }
1120 // Find GROUP BY
1121 $reg = [];
1122 if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $foreignTableClause, $reg)) {
1123 $foreignTableClauseArray['GROUPBY'] = QueryHelper::parseGroupBy(trim($reg[2]));
1124 $foreignTableClause = $reg[1];
1125 }
1126 // Rest is assumed to be "WHERE" clause
1127 $foreignTableClauseArray['WHERE'] = QueryHelper::stripLogicalOperatorPrefix($foreignTableClause);
1128
1129 return $foreignTableClauseArray;
1130 }
1131
1132 /**
1133 * Convert the current database values into an array
1134 *
1135 * @param array $row database row
1136 * @param string $fieldName fieldname to process
1137 * @return array
1138 */
1139 protected function processDatabaseFieldValue(array $row, $fieldName)
1140 {
1141 $currentDatabaseValues = array_key_exists($fieldName, $row)
1142 ? $row[$fieldName]
1143 : '';
1144 return GeneralUtility::trimExplode(',', $currentDatabaseValues, true);
1145 }
1146
1147 /**
1148 * Validate and sanitize database row values of the select field with the given name.
1149 * Creates an array out of databaseRow[selectField] values.
1150 *
1151 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1152 *
1153 * @param array $result The current result array.
1154 * @param string $fieldName Name of the current select field.
1155 * @param array $staticValues Array with statically defined items, item value is used as array key.
1156 * @return array
1157 */
1158 protected function processSelectFieldValue(array $result, $fieldName, array $staticValues)
1159 {
1160 $fieldConfig = $result['processedTca']['columns'][$fieldName];
1161
1162 $currentDatabaseValueArray = array_key_exists($fieldName, $result['databaseRow']) ? $result['databaseRow'][$fieldName] : [];
1163 $newDatabaseValueArray = [];
1164
1165 // Add all values that were defined by static methods and do not come from the relation
1166 // e.g. TCA, TSconfig, itemProcFunc etc.
1167 foreach ($currentDatabaseValueArray as $value) {
1168 if (isset($staticValues[$value])) {
1169 $newDatabaseValueArray[] = $value;
1170 }
1171 }
1172
1173 if (isset($fieldConfig['config']['foreign_table']) && !empty($fieldConfig['config']['foreign_table'])) {
1174 /** @var RelationHandler $relationHandler */
1175 $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
1176 $relationHandler->registerNonTableValues = !empty($fieldConfig['config']['allowNonIdValues']);
1177 if (!empty($fieldConfig['config']['MM']) && $result['command'] !== 'new') {
1178 // MM relation
1179 $relationHandler->start(
1180 implode(',', $currentDatabaseValueArray),
1181 $fieldConfig['config']['foreign_table'],
1182 $fieldConfig['config']['MM'],
1183 $result['databaseRow']['uid'],
1184 $result['tableName'],
1185 $fieldConfig['config']
1186 );
1187 } else {
1188 // Non MM relation
1189 // If not dealing with MM relations, use default live uid, not versioned uid for record relations
1190 $relationHandler->start(
1191 implode(',', $currentDatabaseValueArray),
1192 $fieldConfig['config']['foreign_table'],
1193 '',
1194 $this->getLiveUid($result),
1195 $result['tableName'],
1196 $fieldConfig['config']
1197 );
1198 }
1199 $newDatabaseValueArray = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
1200 }
1201
1202 return array_unique($newDatabaseValueArray);
1203 }
1204
1205 /**
1206 * Translate the item labels
1207 *
1208 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1209 *
1210 * @param array $result Result array
1211 * @param array $itemArray Items
1212 * @param string $table
1213 * @param string $fieldName
1214 * @return array
1215 */
1216 public function translateLabels(array $result, array $itemArray, $table, $fieldName)
1217 {
1218 $languageService = $this->getLanguageService();
1219
1220 foreach ($itemArray as $key => $item) {
1221 if (!isset($dynamicItems[$key])) {
1222 $staticValues[$item[1]] = $item;
1223 }
1224 if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1225 && !empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
1226 ) {
1227 $label = $languageService->sL($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]]);
1228 } else {
1229 $label = $languageService->sL(trim($item[0]));
1230 }
1231 $value = strlen((string)$item[1]) > 0 ? $item[1] : '';
1232 $icon = $item[2] ?: null;
1233 $helpText = $item[3] ?: null;
1234 $itemArray[$key] = [
1235 $label,
1236 $value,
1237 $icon,
1238 $helpText
1239 ];
1240 }
1241
1242 return $itemArray;
1243 }
1244
1245 /**
1246 * Sanitize incoming item array
1247 *
1248 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1249 *
1250 * @param mixed $itemArray
1251 * @param string $tableName
1252 * @param string $fieldName
1253 * @throws \UnexpectedValueException
1254 * @return array
1255 */
1256 public function sanitizeItemArray($itemArray, $tableName, $fieldName)
1257 {
1258 if (!is_array($itemArray)) {
1259 $itemArray = [];
1260 }
1261 foreach ($itemArray as $item) {
1262 if (!is_array($item)) {
1263 throw new \UnexpectedValueException(
1264 'An item in field ' . $fieldName . ' of table ' . $tableName . ' is not an array as expected',
1265 1439288036
1266 );
1267 }
1268 }
1269
1270 return $itemArray;
1271 }
1272
1273 /**
1274 * Make sure maxitems is always filled with a valid integer value.
1275 *
1276 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1277 *
1278 * @param mixed $maxItems
1279 * @return int
1280 */
1281 public function sanitizeMaxItems($maxItems)
1282 {
1283 if (!empty($maxItems)
1284 && (int)$maxItems > 1
1285 ) {
1286 $maxItems = (int)$maxItems;
1287 } else {
1288 $maxItems = 1;
1289 }
1290
1291 return $maxItems;
1292 }
1293
1294 /**
1295 * Gets the record uid of the live default record. If already
1296 * pointing to the live record, the submitted record uid is returned.
1297 *
1298 * @param array $result Result array
1299 * @return int
1300 * @throws \UnexpectedValueException
1301 */
1302 protected function getLiveUid(array $result)
1303 {
1304 $table = $result['tableName'];
1305 $row = $result['databaseRow'];
1306 $uid = $row['uid'];
1307 if (!empty($result['processedTca']['ctrl']['versioningWS'])
1308 && $result['pid'] === -1
1309 ) {
1310 if (empty($row['t3ver_oid'])) {
1311 throw new \UnexpectedValueException(
1312 'No t3ver_oid found for record ' . $row['uid'] . ' on table ' . $table,
1313 1440066481
1314 );
1315 }
1316 $uid = $row['t3ver_oid'];
1317 }
1318 return $uid;
1319 }
1320
1321 /**
1322 * Determine the static values in the item array
1323 *
1324 * Used by TcaSelectItems and TcaSelectTreeItems data providers
1325 *
1326 * @param array $itemArray All item records for the select field
1327 * @param array $dynamicItemArray Item records from dynamic sources
1328 * @return array
1329 */
1330 public function getStaticValues($itemArray, $dynamicItemArray)
1331 {
1332 $staticValues = [];
1333 foreach ($itemArray as $key => $item) {
1334 if (!isset($dynamicItemArray[$key])) {
1335 $staticValues[$item[1]] = $item;
1336 }
1337 }
1338 return $staticValues;
1339 }
1340
1341 /**
1342 * @return LanguageService
1343 */
1344 protected function getLanguageService()
1345 {
1346 return $GLOBALS['LANG'];
1347 }
1348
1349 /**
1350 * @return BackendUserAuthentication
1351 */
1352 protected function getBackendUser()
1353 {
1354 return $GLOBALS['BE_USER'];
1355 }
1356 }