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