Passed
Push — master ( 6d956e...2f0c26 )
by
unknown
13:12
created

AbstractItemProvider::addItemsFromFolder()   C

Complexity

Conditions 15
Paths 55

Size

Total Lines 58
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 39
dl 0
loc 58
rs 5.9166
c 0
b 0
f 0
cc 15
nc 55
nop 3

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Backend\Form\FormDataProvider;
17
18
use Doctrine\DBAL\Exception as DBALException;
19
use TYPO3\CMS\Backend\Utility\BackendUtility;
20
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21
use TYPO3\CMS\Core\Database\ConnectionPool;
22
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
23
use TYPO3\CMS\Core\Database\Query\QueryHelper;
24
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
25
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
26
use TYPO3\CMS\Core\Database\RelationHandler;
27
use TYPO3\CMS\Core\Hooks\TcaItemsProcessorFunctions;
28
use TYPO3\CMS\Core\Imaging\IconFactory;
29
use TYPO3\CMS\Core\Localization\LanguageService;
30
use TYPO3\CMS\Core\Messaging\FlashMessage;
31
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
32
use TYPO3\CMS\Core\Messaging\FlashMessageService;
33
use TYPO3\CMS\Core\Resource\FileRepository;
34
use TYPO3\CMS\Core\Resource\ResourceStorage;
35
use TYPO3\CMS\Core\Type\Bitmask\Permission;
36
use TYPO3\CMS\Core\Utility\ArrayUtility;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Core\Utility\MathUtility;
39
use TYPO3\CMS\Core\Versioning\VersionState;
40
41
/**
42
 * Contains methods used by Data providers that handle elements
43
 * with single items like select, radio and some more.
44
 */
45
abstract class AbstractItemProvider
46
{
47
    /**
48
     * Resolve "itemProcFunc" of elements.
49
     *
50
     * @param array $result Main result array
51
     * @param string $fieldName Field name to handle item list for
52
     * @param array $items Existing items array
53
     * @return array New list of item elements
54
     */
55
    protected function resolveItemProcessorFunction(array $result, $fieldName, array $items)
56
    {
57
        $table = $result['tableName'];
58
        $config = $result['processedTca']['columns'][$fieldName]['config'];
59
60
        $pageTsProcessorParameters = null;
61
        if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'])) {
62
            $pageTsProcessorParameters = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['itemsProcFunc.'];
63
        }
64
        $processorParameters = [
65
            // Function manipulates $items directly and return nothing
66
            'items' => &$items,
67
            'config' => $config,
68
            'TSconfig' => $pageTsProcessorParameters,
69
            'table' => $table,
70
            'row' => $result['databaseRow'],
71
            'field' => $fieldName,
72
            // IMPORTANT: Below fields are only available in FormEngine context.
73
            // They are not used by the DataHandler when processing itemsProcFunc
74
            // for checking if a submitted value is valid. This means, in case
75
            // an item is added based on one of these fields, it won't be persisted
76
            // by the DataHandler. This currently(!) only concerns columns of type "check"
77
            // and type "radio", see checkValueForCheck() and checkValueForRadio().
78
            // Therefore, no limitations when using those fields with other types
79
            // like "select", but this may change in the future.
80
            'inlineParentUid' => $result['inlineParentUid'],
81
            'inlineParentTableName' => $result['inlineParentTableName'],
82
            'inlineParentFieldName' => $result['inlineParentFieldName'],
83
            'inlineParentConfig' => $result['inlineParentConfig'],
84
            'inlineTopMostParentUid' => $result['inlineTopMostParentUid'],
85
            'inlineTopMostParentTableName' => $result['inlineTopMostParentTableName'],
86
            'inlineTopMostParentFieldName' => $result['inlineTopMostParentFieldName'],
87
        ];
88
        if (!empty($result['flexParentDatabaseRow'])) {
89
            $processorParameters['flexParentDatabaseRow'] = $result['flexParentDatabaseRow'];
90
        }
91
92
        try {
93
            GeneralUtility::callUserFunction($config['itemsProcFunc'], $processorParameters, $this);
94
        } catch (\Exception $exception) {
95
            // The itemsProcFunc method may throw an exception, create a flash message if so
96
            $languageService = $this->getLanguageService();
0 ignored issues
show
Bug introduced by
The method getLanguageService() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

96
            /** @scrutinizer ignore-call */ 
97
            $languageService = $this->getLanguageService();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
97
            $fieldLabel = $fieldName;
98
            if (!empty($result['processedTca']['columns'][$fieldName]['label'])) {
99
                $fieldLabel = $languageService->sL($result['processedTca']['columns'][$fieldName]['label']);
100
            }
101
            $message = sprintf(
102
                $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:error.items_proc_func_error'),
103
                $fieldLabel,
104
                $exception->getMessage()
105
            );
106
            /** @var FlashMessage $flashMessage */
107
            $flashMessage = GeneralUtility::makeInstance(
108
                FlashMessage::class,
109
                $message,
110
                '',
111
                FlashMessage::ERROR,
112
                true
113
            );
114
            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
115
            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
116
            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
117
            $defaultFlashMessageQueue->enqueue($flashMessage);
118
        }
119
120
        return $items;
121
    }
122
123
    /**
124
     * PageTsConfig addItems:
125
     *
126
     * TCEFORMS.aTable.aField[.types][.aType].addItems.aValue = aLabel,
127
     * with type specific options merged by pageTsConfig already
128
     *
129
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
130
     *
131
     * @param array $result result array
132
     * @param string $fieldName Current handle field name
133
     * @param array $items Incoming items
134
     * @return array Modified item array
135
     */
136
    protected function addItemsFromPageTsConfig(array $result, $fieldName, array $items)
137
    {
138
        $table = $result['tableName'];
139
        if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
140
            && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'])
141
        ) {
142
            $addItemsArray = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['addItems.'];
143
            foreach ($addItemsArray as $value => $label) {
144
                // If the value ends with a dot, it is a subelement like "34.icon = mylabel.png", skip it
145
                if (substr($value, -1) === '.') {
146
                    continue;
147
                }
148
                // Check if value "34 = mylabel" also has a "34.icon = myImage.png"
149
                $iconIdentifier = null;
150
                if (isset($addItemsArray[$value . '.'])
151
                    && is_array($addItemsArray[$value . '.'])
152
                    && !empty($addItemsArray[$value . '.']['icon'])
153
                ) {
154
                    $iconIdentifier = $addItemsArray[$value . '.']['icon'];
155
                }
156
                $items[] = [$label, $value, $iconIdentifier];
157
            }
158
        }
159
        return $items;
160
    }
161
162
    /**
163
     * TCA config "special" evaluation. Add them to $items
164
     *
165
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
166
     *
167
     * @param array $result Result array
168
     * @param string $fieldName Current handle field name
169
     * @param array $items Incoming items
170
     * @return array Modified item array
171
     * @throws \UnexpectedValueException
172
     * @deprecated since v11, will be removed in v12
173
     */
174
    protected function addItemsFromSpecial(array $result, $fieldName, array $items)
175
    {
176
        // Guard
177
        if (empty($result['processedTca']['columns'][$fieldName]['config']['special'])
178
            || !is_string($result['processedTca']['columns'][$fieldName]['config']['special'])
179
        ) {
180
            return $items;
181
        }
182
183
        $special = $result['processedTca']['columns'][$fieldName]['config']['special'];
184
185
        trigger_error(
186
            'Using the TCA property \'special=' . $special . '\' is deprecated and will be removed in TYPO3 v12. Use a custom itemsProcFunc instead.',
187
            E_USER_DEPRECATED
188
        );
189
190
        switch (true) {
191
            case $special === 'tables':
192
                $fieldInformation = ['items' => $items];
193
                (new TcaItemsProcessorFunctions())->populateAvailableTables($fieldInformation);
194
                $items = $fieldInformation['items'];
195
                break;
196
            case $special === 'pagetypes':
197
                $fieldInformation = ['items' => $items];
198
                (new TcaItemsProcessorFunctions())->populateAvailablePageTypes($fieldInformation);
199
                $items = $fieldInformation['items'];
200
                break;
201
            case $special === 'exclude':
202
                $fieldInformation = ['items' => $items];
203
                (new TcaItemsProcessorFunctions())->populateExcludeFields($fieldInformation);
204
                $items = $fieldInformation['items'];
205
                break;
206
            case $special === 'explicitValues':
207
                $fieldInformation = ['items' => $items];
208
                (new TcaItemsProcessorFunctions())->populateExplicitAuthValues($fieldInformation);
209
                $items = $fieldInformation['items'];
210
                break;
211
            case $special === 'custom':
212
                $fieldInformation = ['items' => $items];
213
                (new TcaItemsProcessorFunctions())->populateCustomPermissionOptions($fieldInformation);
214
                $items = $fieldInformation['items'];
215
                break;
216
            case $special === 'modListGroup':
217
                $fieldInformation = ['items' => $items];
218
                (new TcaItemsProcessorFunctions())->populateAvailableGroupModules($fieldInformation);
219
                $items = $fieldInformation['items'];
220
                break;
221
            case $special === 'modListUser':
222
                $fieldInformation = ['items' => $items];
223
                (new TcaItemsProcessorFunctions())->populateAvailableUserModules($fieldInformation);
224
                $items = $fieldInformation['items'];
225
                break;
226
            default:
227
                throw new \UnexpectedValueException(
228
                    'Unknown special value ' . $special . ' for field ' . $fieldName . ' of table ' . $result['tableName'],
229
                    1439298496
230
                );
231
        }
232
        return $items;
233
    }
234
235
    /**
236
     * TCA config "fileFolder" evaluation. Add them to $items
237
     *
238
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
239
     *
240
     * @param array $result Result array
241
     * @param string $fieldName Current handle field name
242
     * @param array $items Incoming items
243
     * @return array Modified item array
244
     * @throws \RuntimeException
245
     */
246
    protected function addItemsFromFolder(array $result, $fieldName, array $items)
247
    {
248
        if (empty($result['processedTca']['columns'][$fieldName]['config']['fileFolderConfig']['folder'])
249
            || !is_string($result['processedTca']['columns'][$fieldName]['config']['fileFolderConfig']['folder'])
250
        ) {
251
            return $items;
252
        }
253
254
        $tableName = $result['tableName'];
255
        $fileFolderConfig = $result['processedTca']['columns'][$fieldName]['config']['fileFolderConfig'];
256
        $fileFolderTSconfig = $result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['config.']['fileFolderConfig.'] ?? [];
257
258
        if (is_array($fileFolderTSconfig) && $fileFolderTSconfig !== []) {
259
            if ($fileFolderTSconfig['folder'] ?? false) {
260
                $fileFolderConfig['folder'] = $fileFolderTSconfig['folder'];
261
            }
262
            if (isset($fileFolderTSconfig['allowedExtensions'])) {
263
                $fileFolderConfig['allowedExtensions'] = $fileFolderTSconfig['allowedExtensions'];
264
            }
265
            if (isset($fileFolderTSconfig['depth'])) {
266
                $fileFolderConfig['depth'] = (int)$fileFolderTSconfig['depth'];
267
            }
268
        }
269
270
        $folderRaw = $fileFolderConfig['folder'];
271
        $folder = GeneralUtility::getFileAbsFileName($folderRaw);
272
        if ($folder === '') {
273
            throw new \RuntimeException(
274
                'Invalid folder given for item processing: ' . $folderRaw . ' for table ' . $tableName . ', field ' . $fieldName,
275
                1479399227
276
            );
277
        }
278
        $folder = rtrim($folder, '/') . '/';
279
280
        if (@is_dir($folder)) {
281
            $allowedExtensions = '';
282
            if (!empty($fileFolderConfig['allowedExtensions']) && is_string($fileFolderConfig['allowedExtensions'])) {
283
                $allowedExtensions = $fileFolderConfig['allowedExtensions'];
284
            }
285
            $depth = isset($fileFolderConfig['depth'])
286
                ? MathUtility::forceIntegerInRange($fileFolderConfig['depth'], 0, 99)
287
                : 99;
288
            $fileArray = GeneralUtility::getAllFilesAndFoldersInPath([], $folder, $allowedExtensions, false, $depth);
289
            $fileArray = GeneralUtility::removePrefixPathFromList($fileArray, $folder);
290
            foreach ($fileArray as $fileReference) {
291
                $fileInformation = pathinfo($fileReference);
292
                $icon = GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], strtolower($fileInformation['extension']))
293
                    ? $folder . $fileReference
294
                    : '';
295
                $items[] = [
296
                    $fileReference,
297
                    $fileReference,
298
                    $icon
299
                ];
300
            }
301
        }
302
303
        return $items;
304
    }
305
306
    /**
307
     * TCA config "foreign_table" evaluation. Add them to $items
308
     *
309
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
310
     *
311
     * @param array $result Result array
312
     * @param string $fieldName Current handle field name
313
     * @param array $items Incoming items
314
     * @return array Modified item array
315
     * @throws \UnexpectedValueException
316
     */
317
    protected function addItemsFromForeignTable(array $result, $fieldName, array $items)
318
    {
319
        $databaseError = null;
320
        $queryResult = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $queryResult is dead and can be removed.
Loading history...
321
        // Guard
322
        if (empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
323
            || !is_string($result['processedTca']['columns'][$fieldName]['config']['foreign_table'])
324
        ) {
325
            return $items;
326
        }
327
328
        $languageService = $this->getLanguageService();
329
330
        $foreignTable = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'];
331
332
        if (!isset($GLOBALS['TCA'][$foreignTable]) || !is_array($GLOBALS['TCA'][$foreignTable])) {
333
            throw new \UnexpectedValueException(
334
                'Field ' . $fieldName . ' of table ' . $result['tableName'] . ' reference to foreign table '
335
                . $foreignTable . ', but this table is not defined in TCA',
336
                1439569743
337
            );
338
        }
339
340
        $queryBuilder = $this->buildForeignTableQueryBuilder($result, $fieldName);
341
        try {
342
            $queryResult = $queryBuilder->execute();
343
        } catch (DBALException $e) {
344
            $databaseError = $e->getPrevious()->getMessage();
345
        }
346
347
        // Early return on error with flash message
348
        if (!empty($databaseError)) {
349
            $msg = $databaseError . '. ';
350
            $msg .= $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:error.database_schema_mismatch');
351
            $msgTitle = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:error.database_schema_mismatch_title');
352
            /** @var FlashMessage $flashMessage */
353
            $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $msg, $msgTitle, FlashMessage::ERROR, true);
354
            /** @var FlashMessageService $flashMessageService */
355
            $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
356
            /** @var FlashMessageQueue $defaultFlashMessageQueue */
357
            $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
358
            $defaultFlashMessageQueue->enqueue($flashMessage);
359
            return $items;
360
        }
361
362
        $labelPrefix = '';
363
        if (!empty($result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'])) {
364
            $labelPrefix = $result['processedTca']['columns'][$fieldName]['config']['foreign_table_prefix'];
365
            $labelPrefix = $languageService->sL($labelPrefix);
366
        }
367
368
        $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
369
        $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
370
371
        while ($foreignRow = $queryResult->fetch()) {
372
            BackendUtility::workspaceOL($foreignTable, $foreignRow);
373
            // Only proceed in case the row was not unset and we don't deal with a delete placeholder
374
            if (is_array($foreignRow)
375
                && !VersionState::cast($foreignRow['t3ver_state'] ?? 0)->equals(VersionState::DELETE_PLACEHOLDER)
376
            ) {
377
                // If the foreign table sets selicon_field, this field can contain an image
378
                // that represents this specific row.
379
                $iconFieldName = '';
380
                $isReferenceField = false;
381
                if (!empty($GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'])) {
382
                    $iconFieldName = $GLOBALS['TCA'][$foreignTable]['ctrl']['selicon_field'];
383
                    if (isset($GLOBALS['TCA'][$foreignTable]['columns'][$iconFieldName]['config']['type'])
384
                        && $GLOBALS['TCA'][$foreignTable]['columns'][$iconFieldName]['config']['type'] === 'inline'
385
                        && $GLOBALS['TCA'][$foreignTable]['columns'][$iconFieldName]['config']['foreign_table'] === 'sys_file_reference'
386
                    ) {
387
                        $isReferenceField = true;
388
                    }
389
                }
390
                $icon = '';
391
                if ($isReferenceField) {
392
                    $references = $fileRepository->findByRelation($foreignTable, $iconFieldName, $foreignRow['uid']);
393
                    if (is_array($references) && !empty($references)) {
394
                        $icon = reset($references);
395
                        $icon = $icon->getPublicUrl();
396
                    }
397
                } else {
398
                    // Else, determine icon based on record type, or a generic fallback
399
                    $icon = $iconFactory->mapRecordTypeToIconIdentifier($foreignTable, $foreignRow);
400
                }
401
                // Add the item
402
                $items[] = [
403
                    $labelPrefix . BackendUtility::getRecordTitle($foreignTable, $foreignRow),
404
                    $foreignRow['uid'],
405
                    $icon
406
                ];
407
            }
408
        }
409
410
        return $items;
411
    }
412
413
    /**
414
     * Remove items using "keepItems" pageTsConfig
415
     *
416
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
417
     *
418
     * @param array $result Result array
419
     * @param string $fieldName Current handle field name
420
     * @param array $items Incoming items
421
     * @return array Modified item array
422
     */
423
    protected function removeItemsByKeepItemsPageTsConfig(array $result, $fieldName, array $items)
424
    {
425
        $table = $result['tableName'];
426
        if (!isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
427
            || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'])
428
        ) {
429
            return $items;
430
        }
431
432
        // If keepItems is set but is an empty list all current items get removed
433
        if ($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'] === '') {
434
            return [];
435
        }
436
437
        return ArrayUtility::keepItemsInArray(
438
            $items,
439
            $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['keepItems'],
440
            function ($value) {
441
                return $value[1];
442
            }
443
        );
444
    }
445
446
    /**
447
     * Remove items using "removeItems" pageTsConfig
448
     *
449
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
450
     *
451
     * @param array $result Result array
452
     * @param string $fieldName Current handle field name
453
     * @param array $items Incoming items
454
     * @return array Modified item array
455
     */
456
    protected function removeItemsByRemoveItemsPageTsConfig(array $result, $fieldName, array $items)
457
    {
458
        $table = $result['tableName'];
459
        if (!isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
460
            || !is_string($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'])
461
            || $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'] === ''
462
        ) {
463
            return $items;
464
        }
465
466
        $removeItems = array_flip(GeneralUtility::trimExplode(
467
            ',',
468
            $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['removeItems'],
469
            true
470
        ));
471
        foreach ($items as $key => $itemValues) {
472
            if (isset($removeItems[$itemValues[1]])) {
473
                unset($items[$key]);
474
            }
475
        }
476
477
        return $items;
478
    }
479
480
    /**
481
     * Remove items user restriction on language field
482
     *
483
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
484
     *
485
     * @param array $result Result array
486
     * @param string $fieldName Current handle field name
487
     * @param array $items Incoming items
488
     * @return array Modified item array
489
     */
490
    protected function removeItemsByUserLanguageFieldRestriction(array $result, $fieldName, array $items)
491
    {
492
        // Guard clause returns if not a language field is handled
493
        if (empty($result['processedTca']['ctrl']['languageField'])
494
            || $result['processedTca']['ctrl']['languageField'] !== $fieldName
495
        ) {
496
            return $items;
497
        }
498
499
        $backendUser = $this->getBackendUser();
500
        foreach ($items as $key => $itemValues) {
501
            if (!$backendUser->checkLanguageAccess($itemValues[1])) {
502
                unset($items[$key]);
503
            }
504
        }
505
506
        return $items;
507
    }
508
509
    /**
510
     * Remove items by user restriction on authMode items
511
     *
512
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
513
     *
514
     * @param array $result Result array
515
     * @param string $fieldName Current handle field name
516
     * @param array $items Incoming items
517
     * @return array Modified item array
518
     */
519
    protected function removeItemsByUserAuthMode(array $result, $fieldName, array $items)
520
    {
521
        // Guard clause returns early if no authMode field is configured
522
        if (!isset($result['processedTca']['columns'][$fieldName]['config']['authMode'])
523
            || !is_string($result['processedTca']['columns'][$fieldName]['config']['authMode'])
524
        ) {
525
            return $items;
526
        }
527
528
        $backendUser = $this->getBackendUser();
529
        $authMode = $result['processedTca']['columns'][$fieldName]['config']['authMode'];
530
        foreach ($items as $key => $itemValues) {
531
            // @todo: checkAuthMode() uses $GLOBAL access for "individual" authMode - get rid of this
532
            if (!$backendUser->checkAuthMode($result['tableName'], $fieldName, $itemValues[1], $authMode)) {
533
                unset($items[$key]);
534
            }
535
        }
536
537
        return $items;
538
    }
539
540
    /**
541
     * Remove items if doktype is handled for non admin users
542
     *
543
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
544
     *
545
     * @param array $result Result array
546
     * @param string $fieldName Current handle field name
547
     * @param array $items Incoming items
548
     * @return array Modified item array
549
     */
550
    protected function removeItemsByDoktypeUserRestriction(array $result, $fieldName, array $items)
551
    {
552
        $table = $result['tableName'];
553
        $backendUser = $this->getBackendUser();
554
        // Guard clause returns if not correct table and field or if user is admin
555
        if ($table !== 'pages' || $fieldName !== 'doktype' || $backendUser->isAdmin()
556
        ) {
557
            return $items;
558
        }
559
560
        $allowedPageTypes = $backendUser->groupData['pagetypes_select'];
561
        foreach ($items as $key => $itemValues) {
562
            if (!GeneralUtility::inList($allowedPageTypes, $itemValues[1])) {
563
                unset($items[$key]);
564
            }
565
        }
566
567
        return $items;
568
    }
569
570
    /**
571
     * Remove items if sys_file_storage is not allowed for non-admin users.
572
     *
573
     * Used by TcaSelectItems data providers
574
     *
575
     * @param array $result Result array
576
     * @param string $fieldName Current handle field name
577
     * @param array $items Incoming items
578
     * @return array Modified item array
579
     */
580
    protected function removeItemsByUserStorageRestriction(array $result, $fieldName, array $items)
581
    {
582
        $referencedTableName = $result['processedTca']['columns'][$fieldName]['config']['foreign_table'] ?? null;
583
        if ($referencedTableName !== 'sys_file_storage') {
584
            return $items;
585
        }
586
587
        $allowedStorageIds = array_map(
588
            function (ResourceStorage $storage) {
589
                return $storage->getUid();
590
            },
591
            $this->getBackendUser()->getFileStorages()
592
        );
593
594
        return array_filter(
595
            $items,
596
            function (array $item) use ($allowedStorageIds) {
597
                $itemValue = $item[1] ?? null;
598
                return empty($itemValue)
599
                    || in_array((int)$itemValue, $allowedStorageIds, true);
600
            }
601
        );
602
    }
603
604
    /**
605
     * Build query to fetch foreign records. Helper method of
606
     * addItemsFromForeignTable(), do not call otherwise.
607
     *
608
     * @param array $result Result array
609
     * @param string $localFieldName Current handle field name
610
     * @return QueryBuilder
611
     */
612
    protected function buildForeignTableQueryBuilder(array $result, string $localFieldName): QueryBuilder
613
    {
614
        $backendUser = $this->getBackendUser();
615
616
        $foreignTableName = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table'];
617
        $foreignTableClauseArray = $this->processForeignTableClause($result, $foreignTableName, $localFieldName);
618
619
        $fieldList = BackendUtility::getCommonSelectFields($foreignTableName, $foreignTableName . '.');
620
        /** @var QueryBuilder $queryBuilder */
621
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
622
            ->getQueryBuilderForTable($foreignTableName);
623
624
        $queryBuilder->getRestrictions()
625
            ->removeAll()
626
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
627
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $this->getBackendUser()->workspace));
628
629
        $queryBuilder
630
            ->select(...GeneralUtility::trimExplode(',', $fieldList, true))
631
            ->from($foreignTableName)
632
            ->where($foreignTableClauseArray['WHERE']);
633
634
        if (!empty($foreignTableClauseArray['GROUPBY'])) {
635
            $queryBuilder->groupBy(...$foreignTableClauseArray['GROUPBY']);
636
        }
637
638
        if (!empty($foreignTableClauseArray['ORDERBY'])) {
639
            foreach ($foreignTableClauseArray['ORDERBY'] as $orderPair) {
640
                [$fieldName, $order] = $orderPair;
641
                $queryBuilder->addOrderBy($fieldName, $order);
642
            }
643
        } elseif (!empty($GLOBALS['TCA'][$foreignTableName]['ctrl']['default_sortby'])) {
644
            $orderByClauses = QueryHelper::parseOrderBy($GLOBALS['TCA'][$foreignTableName]['ctrl']['default_sortby']);
645
            foreach ($orderByClauses as $orderByClause) {
646
                if (!empty($orderByClause[0])) {
647
                    $queryBuilder->addOrderBy($foreignTableName . '.' . $orderByClause[0], $orderByClause[1]);
648
                }
649
            }
650
        }
651
652
        if (!empty($foreignTableClauseArray['LIMIT'])) {
653
            if (!empty($foreignTableClauseArray['LIMIT'][1])) {
654
                $queryBuilder->setMaxResults($foreignTableClauseArray['LIMIT'][1]);
655
                $queryBuilder->setFirstResult($foreignTableClauseArray['LIMIT'][0]);
656
            } elseif (!empty($foreignTableClauseArray['LIMIT'][0])) {
657
                $queryBuilder->setMaxResults($foreignTableClauseArray['LIMIT'][0]);
658
            }
659
        }
660
661
        // rootLevel = -1 means that elements can be on the rootlevel OR on any page (pid!=-1)
662
        // rootLevel = 0 means that elements are not allowed on root level
663
        // rootLevel = 1 means that elements are only on the root level (pid=0)
664
        $rootLevel = 0;
665
        if (isset($GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'])) {
666
            $rootLevel = (int)$GLOBALS['TCA'][$foreignTableName]['ctrl']['rootLevel'];
667
        }
668
669
        if ($rootLevel === -1) {
670
            $queryBuilder->andWhere(
671
                $queryBuilder->expr()->neq(
672
                    $foreignTableName . '.pid',
673
                    $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
674
                )
675
            );
676
        } elseif ($rootLevel === 1) {
677
            $queryBuilder->andWhere(
678
                $queryBuilder->expr()->eq(
679
                    $foreignTableName . '.pid',
680
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
681
                )
682
            );
683
        } else {
684
            $queryBuilder->andWhere($backendUser->getPagePermsClause(Permission::PAGE_SHOW));
685
            if ($foreignTableName !== 'pages') {
686
                $queryBuilder
687
                    ->from('pages')
688
                    ->andWhere(
689
                        $queryBuilder->expr()->eq(
690
                            'pages.uid',
691
                            $queryBuilder->quoteIdentifier($foreignTableName . '.pid')
692
                        )
693
                    );
694
            }
695
        }
696
697
        // @todo what about PID restriction?
698
        if ($this->getBackendUser()->workspace !== 0 && BackendUtility::isTableWorkspaceEnabled($foreignTableName)) {
699
            $queryBuilder
700
                ->andWhere(
701
                    $queryBuilder->expr()->neq(
702
                        $foreignTableName . '.t3ver_state',
703
                        $queryBuilder->createNamedParameter(VersionState::MOVE_POINTER, \PDO::PARAM_INT)
704
                    )
705
                );
706
        }
707
708
        return $queryBuilder;
709
    }
710
711
    /**
712
     * Replace markers in a where clause from TCA foreign_table_where
713
     *
714
     * ###REC_FIELD_[field name]###
715
     * ###THIS_UID### - is current element uid (zero if new).
716
     * ###CURRENT_PID### - is the current page id (pid of the record).
717
     * ###SITEROOT###
718
     * ###PAGE_TSCONFIG_ID### - a value you can set from Page TSconfig dynamically.
719
     * ###PAGE_TSCONFIG_IDLIST### - a value you can set from Page TSconfig dynamically.
720
     * ###PAGE_TSCONFIG_STR### - a value you can set from Page TSconfig dynamically.
721
     *
722
     * @param array $result Result array
723
     * @param string $foreignTableName Name of foreign table
724
     * @param string $localFieldName Current handle field name
725
     * @return array Query parts with keys WHERE, ORDERBY, GROUPBY, LIMIT
726
     */
727
    protected function processForeignTableClause(array $result, $foreignTableName, $localFieldName)
728
    {
729
        $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($foreignTableName);
730
        $localTable = $result['tableName'];
731
        $effectivePid = $result['effectivePid'];
732
733
        $foreignTableClause = '';
734
        if (!empty($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
735
            && is_string($result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'])
736
        ) {
737
            $foreignTableClause = $result['processedTca']['columns'][$localFieldName]['config']['foreign_table_where'];
738
            // Replace possible markers in query
739
            if (strpos($foreignTableClause, '###REC_FIELD_') !== false) {
740
                // " AND table.field='###REC_FIELD_field1###' AND ..." -> array(" AND table.field='", "field1###' AND ...")
741
                $whereClauseParts = explode('###REC_FIELD_', $foreignTableClause);
742
                foreach ($whereClauseParts as $key => $value) {
743
                    if ($key !== 0) {
744
                        // "field1###' AND ..." -> array("field1", "' AND ...")
745
                        $whereClauseSubParts = explode('###', $value, 2);
746
                        // @todo: Throw exception if there is no value? What happens for NEW records?
747
                        $databaseRowKey = empty($result['flexParentDatabaseRow']) ? 'databaseRow' : 'flexParentDatabaseRow';
748
                        $rowFieldValue = $result[$databaseRowKey][$whereClauseSubParts[0]] ?? '';
749
                        if (is_array($rowFieldValue)) {
750
                            // If a select or group field is used here, it may have been processed already and
751
                            // is now an array containing uid + table + title + row.
752
                            // See TcaGroup data provider for details.
753
                            // Pick the first one (always on 0), and use uid only.
754
                            $rowFieldValue = $rowFieldValue[0]['uid'] ?? $rowFieldValue[0];
755
                        }
756
                        if (substr($whereClauseParts[0], -1) === '\'' && $whereClauseSubParts[1][0] === '\'') {
757
                            $whereClauseParts[0] = substr($whereClauseParts[0], 0, -1);
758
                            $whereClauseSubParts[1] = substr($whereClauseSubParts[1], 1);
759
                        }
760
                        $whereClauseParts[$key] = $connection->quote($rowFieldValue) . $whereClauseSubParts[1];
761
                    }
762
                }
763
                $foreignTableClause = implode('', $whereClauseParts);
764
            }
765
            if (strpos($foreignTableClause, '###CURRENT_PID###') !== false) {
766
                // Use pid from parent page clause if in flex form context
767
                if (!empty($result['flexParentDatabaseRow']['pid'])) {
768
                    $effectivePid = $result['flexParentDatabaseRow']['pid'];
769
                } elseif (!$effectivePid && !empty($result['databaseRow']['pid'])) {
770
                    // Use pid from database row if in inline context
771
                    $effectivePid = $result['databaseRow']['pid'];
772
                }
773
            }
774
775
            $siteRootUid = 0;
776
            foreach ($result['rootline'] as $rootlinePage) {
777
                if (!empty($rootlinePage['is_siteroot'])) {
778
                    $siteRootUid = (int)$rootlinePage['uid'];
779
                    break;
780
                }
781
            }
782
783
            $pageTsConfigId = 0;
784
            if (isset($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID'])
785
                && $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID']
786
            ) {
787
                $pageTsConfigId = (int)$result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_ID'];
788
            }
789
790
            $pageTsConfigIdList = 0;
791
            if (isset($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST'])
792
                && $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST']
793
            ) {
794
                $pageTsConfigIdList = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_IDLIST'];
795
            }
796
            $pageTsConfigIdListArray = GeneralUtility::trimExplode(',', $pageTsConfigIdList, true);
797
            $pageTsConfigIdList = [];
798
            foreach ($pageTsConfigIdListArray as $pageTsConfigIdListElement) {
799
                if (MathUtility::canBeInterpretedAsInteger($pageTsConfigIdListElement)) {
800
                    $pageTsConfigIdList[] = (int)$pageTsConfigIdListElement;
801
                }
802
            }
803
            $pageTsConfigIdList = implode(',', $pageTsConfigIdList);
804
805
            $pageTsConfigString = '';
806
            if (isset($result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR'])
807
                && $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR']
808
            ) {
809
                $pageTsConfigString = $result['pageTsConfig']['TCEFORM.'][$localTable . '.'][$localFieldName . '.']['PAGE_TSCONFIG_STR'];
810
                $pageTsConfigString = $connection->quote($pageTsConfigString);
811
            }
812
813
            $foreignTableClause = str_replace(
814
                [
815
                    '###CURRENT_PID###',
816
                    '###THIS_UID###',
817
                    '###SITEROOT###',
818
                    '###PAGE_TSCONFIG_ID###',
819
                    '###PAGE_TSCONFIG_IDLIST###',
820
                    '\'###PAGE_TSCONFIG_STR###\'',
821
                    '###PAGE_TSCONFIG_STR###'
822
                ],
823
                [
824
                    (int)$effectivePid,
825
                    (int)$result['databaseRow']['uid'],
826
                    $siteRootUid,
827
                    $pageTsConfigId,
828
                    $pageTsConfigIdList,
829
                    $pageTsConfigString,
830
                    $pageTsConfigString
831
                ],
832
                $foreignTableClause
833
            );
834
        }
835
836
        // Split the clause into an array with keys WHERE, GROUPBY, ORDERBY, LIMIT
837
        // Prepend a space to make sure "[[:space:]]+" will find a space there for the first element.
838
        $foreignTableClause = ' ' . $foreignTableClause;
839
        $foreignTableClauseArray = [
840
            'WHERE' => '',
841
            'GROUPBY' => '',
842
            'ORDERBY' => '',
843
            'LIMIT' => '',
844
        ];
845
        // Find LIMIT
846
        $reg = [];
847
        if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/is', $foreignTableClause, $reg)) {
848
            $foreignTableClauseArray['LIMIT'] = GeneralUtility::intExplode(',', trim($reg[2]), true);
849
            $foreignTableClause = $reg[1];
850
        }
851
        // Find ORDER BY
852
        $reg = [];
853
        if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._()"]+)$/is', $foreignTableClause, $reg)) {
854
            $foreignTableClauseArray['ORDERBY'] = QueryHelper::parseOrderBy(trim($reg[2]));
855
            $foreignTableClause = $reg[1];
856
        }
857
        // Find GROUP BY
858
        $reg = [];
859
        if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._()"]+)$/is', $foreignTableClause, $reg)) {
860
            $foreignTableClauseArray['GROUPBY'] = QueryHelper::parseGroupBy(trim($reg[2]));
861
            $foreignTableClause = $reg[1];
862
        }
863
        // Rest is assumed to be "WHERE" clause
864
        $foreignTableClauseArray['WHERE'] = QueryHelper::stripLogicalOperatorPrefix($foreignTableClause);
865
866
        return $foreignTableClauseArray;
867
    }
868
869
    /**
870
     * Convert the current database values into an array
871
     *
872
     * @param array $row database row
873
     * @param string $fieldName fieldname to process
874
     * @return array
875
     */
876
    protected function processDatabaseFieldValue(array $row, $fieldName)
877
    {
878
        $currentDatabaseValues = array_key_exists($fieldName, $row)
879
            ? $row[$fieldName]
880
            : '';
881
        if (!is_array($currentDatabaseValues)) {
882
            $currentDatabaseValues = GeneralUtility::trimExplode(',', $currentDatabaseValues, true);
883
        }
884
        return $currentDatabaseValues;
885
    }
886
887
    /**
888
     * Validate and sanitize database row values of the select field with the given name.
889
     * Creates an array out of databaseRow[selectField] values.
890
     *
891
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
892
     *
893
     * @param array $result The current result array.
894
     * @param string $fieldName Name of the current select field.
895
     * @param array $staticValues Array with statically defined items, item value is used as array key.
896
     * @return array
897
     */
898
    protected function processSelectFieldValue(array $result, $fieldName, array $staticValues)
899
    {
900
        $fieldConfig = $result['processedTca']['columns'][$fieldName];
901
902
        $currentDatabaseValueArray = array_key_exists($fieldName, $result['databaseRow']) ? $result['databaseRow'][$fieldName] : [];
903
        $newDatabaseValueArray = [];
904
905
        // Add all values that were defined by static methods and do not come from the relation
906
        // e.g. TCA, TSconfig, itemProcFunc etc.
907
        foreach ($currentDatabaseValueArray as $value) {
908
            if (isset($staticValues[$value])) {
909
                $newDatabaseValueArray[] = $value;
910
            }
911
        }
912
913
        if (isset($fieldConfig['config']['foreign_table']) && !empty($fieldConfig['config']['foreign_table'])) {
914
            /** @var RelationHandler $relationHandler */
915
            $relationHandler = GeneralUtility::makeInstance(RelationHandler::class);
916
            $relationHandler->registerNonTableValues = !empty($fieldConfig['config']['allowNonIdValues']);
917
            if (!empty($fieldConfig['config']['MM']) && $result['command'] !== 'new') {
918
                // MM relation
919
                $relationHandler->start(
920
                    implode(',', $currentDatabaseValueArray),
921
                    $fieldConfig['config']['foreign_table'],
922
                    $fieldConfig['config']['MM'],
923
                    $result['databaseRow']['uid'],
924
                    $result['tableName'],
925
                    $fieldConfig['config']
926
                );
927
                $newDatabaseValueArray = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
928
            } else {
929
                // Non MM relation
930
                // If not dealing with MM relations, use default live uid, not versioned uid for record relations
931
                $relationHandler->start(
932
                    implode(',', $currentDatabaseValueArray),
933
                    $fieldConfig['config']['foreign_table'],
934
                    '',
935
                    $this->getLiveUid($result),
936
                    $result['tableName'],
937
                    $fieldConfig['config']
938
                );
939
                $databaseIds = array_merge($newDatabaseValueArray, $relationHandler->getValueArray());
940
                // remove all items from the current DB values if not available as relation or static value anymore
941
                $newDatabaseValueArray = array_values(array_intersect($currentDatabaseValueArray, $databaseIds));
942
            }
943
        }
944
945
        if ($fieldConfig['config']['multiple'] ?? false) {
946
            return $newDatabaseValueArray;
947
        }
948
        return array_unique($newDatabaseValueArray);
949
    }
950
951
    /**
952
     * Translate the item labels
953
     *
954
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
955
     *
956
     * @param array $result Result array
957
     * @param array $itemArray Items
958
     * @param string $table
959
     * @param string $fieldName
960
     * @return array
961
     */
962
    public function translateLabels(array $result, array $itemArray, $table, $fieldName)
963
    {
964
        $languageService = $this->getLanguageService();
965
966
        foreach ($itemArray as $key => $item) {
967
            if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
968
                && !empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]])
969
            ) {
970
                $label = $languageService->sL($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altLabels.'][$item[1]]);
971
            } else {
972
                $label = $languageService->sL(trim($item[0]));
973
            }
974
            $value = strlen((string)$item[1]) > 0 ? $item[1] : '';
975
            $icon = !empty($item[2]) ? $item[2] : null;
976
            $groupId = $item[3] ?? null;
977
            $helpText = null;
978
            if (!empty($item[4])) {
979
                if (\is_string($item[4])) {
980
                    $helpText = $languageService->sL($item[4]);
981
                } else {
982
                    $helpText = $item[4];
983
                }
984
            }
985
            $itemArray[$key] = [
986
                $label,
987
                $value,
988
                $icon,
989
                $groupId,
990
                $helpText
991
            ];
992
        }
993
994
        return $itemArray;
995
    }
996
997
    /**
998
     * Add alternative icon using "altIcons" TSconfig
999
     *
1000
     * @param array $result
1001
     * @param array $items
1002
     * @param string $table
1003
     * @param string $fieldName
1004
     *
1005
     * @return array
1006
     */
1007
    public function addIconFromAltIcons(array $result, array $items, string $table, string $fieldName): array
1008
    {
1009
        foreach ($items as &$item) {
1010
            if (isset($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altIcons.'][$item[1]])
1011
                && !empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altIcons.'][$item[1]])
1012
            ) {
1013
                $item[2] = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.']['altIcons.'][$item[1]];
1014
            }
1015
        }
1016
1017
        return $items;
1018
    }
1019
1020
    /**
1021
     * Sanitize incoming item array
1022
     *
1023
     * Used by TcaSelectItems and TcaSelectTreeItems data providers
1024
     *
1025
     * @param mixed $itemArray
1026
     * @param string $tableName
1027
     * @param string $fieldName
1028
     * @throws \UnexpectedValueException
1029
     * @return array
1030
     */
1031
    public function sanitizeItemArray($itemArray, $tableName, $fieldName)
1032
    {
1033
        if (!is_array($itemArray)) {
1034
            $itemArray = [];
1035
        }
1036
        foreach ($itemArray as $item) {
1037
            if (!is_array($item)) {
1038
                throw new \UnexpectedValueException(
1039
                    'An item in field ' . $fieldName . ' of table ' . $tableName . ' is not an array as expected',
1040
                    1439288036
1041
                );
1042
            }
1043
        }
1044
1045
        return $itemArray;
1046
    }
1047
1048
    /**
1049
     * Gets the record uid of the live default record. If already
1050
     * pointing to the live record, the submitted record uid is returned.
1051
     *
1052
     * @param array $result Result array
1053
     * @return int|string If the record is new, uid will be a string beginning with "NEW". Otherwise an int.
1054
     * @throws \UnexpectedValueException
1055
     */
1056
    protected function getLiveUid(array $result)
1057
    {
1058
        $table = $result['tableName'];
1059
        $row = $result['databaseRow'];
1060
        $uid = $row['uid'] ?? 0;
1061
        if (BackendUtility::isTableWorkspaceEnabled($table) && (int)($row['t3ver_oid'] ?? 0) > 0) {
1062
            $uid = $row['t3ver_oid'];
1063
        }
1064
        return $uid;
1065
    }
1066
1067
    /**
1068
     * @return LanguageService
1069
     */
1070
    protected function getLanguageService()
1071
    {
1072
        return $GLOBALS['LANG'];
1073
    }
1074
1075
    /**
1076
     * @return BackendUserAuthentication
1077
     */
1078
    protected function getBackendUser()
1079
    {
1080
        return $GLOBALS['BE_USER'];
1081
    }
1082
}
1083