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