Passed
Push — master ( e0e49e...fca4b6 )
by
unknown
14:23
created

getGroupedExplicitAuthFieldValues()   D

Complexity

Conditions 19
Paths 3

Size

Total Lines 64
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 42
dl 0
loc 64
rs 4.5166
c 1
b 0
f 0
cc 19
nc 3
nop 0

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
declare(strict_types=1);
4
5
/*
6
 * This file is part of the TYPO3 CMS project.
7
 *
8
 * It is free software; you can redistribute it and/or modify it under
9
 * the terms of the GNU General Public License, either version 2
10
 * of the License, or any later version.
11
 *
12
 * For the full copyright and license information, please read the
13
 * LICENSE.txt file that was distributed with this source code.
14
 *
15
 * The TYPO3 project - inspiring people to share!
16
 */
17
18
namespace TYPO3\CMS\Core\Hooks;
19
20
use TYPO3\CMS\Backend\Module\ModuleLoader;
21
use TYPO3\CMS\Core\Configuration\FlexForm\Exception\InvalidIdentifierException;
22
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
23
use TYPO3\CMS\Core\Imaging\IconFactory;
24
use TYPO3\CMS\Core\Imaging\IconRegistry;
25
use TYPO3\CMS\Core\Localization\LanguageService;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
28
/**
29
 * Various items processor functions, mainly used for special select fields in `be_users` and `be_groups`
30
 *
31
 * @internal This class is a hook implementation and is not part of the TYPO3 Core API.
32
 */
33
class TcaItemsProcessorFunctions
34
{
35
    protected IconFactory $iconFactory;
36
    protected IconRegistry $iconRegistry;
37
38
    public function __construct()
39
    {
40
        $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
41
        $this->iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
42
    }
43
44
    public function populateAvailableTables(array &$fieldDefinition): void
45
    {
46
        foreach ($GLOBALS['TCA'] as $tableName => $tableConfiguration) {
47
            if ($tableConfiguration['ctrl']['adminOnly'] ?? false) {
48
                // Hide "admin only" tables
49
                continue;
50
            }
51
            $label = ($tableConfiguration['ctrl']['title'] ?? '') ?: '';
52
            $icon = $this->iconFactory->mapRecordTypeToIconIdentifier($tableName, []);
53
            $this->getLanguageService()->loadSingleTableDescription($tableName);
54
            $helpText = (string)($GLOBALS['TCA_DESCR'][$tableName]['columns']['']['description'] ?? '');
55
            $fieldDefinition['items'][] = [$label, $tableName, $icon, null, $helpText];
56
        }
57
    }
58
59
    public function populateAvailablePageTypes(array &$fieldDefinition): void
60
    {
61
        $pageTypes = $GLOBALS['TCA']['pages']['columns']['doktype']['config']['items'] ?? [];
62
        if (is_array($pageTypes) && $pageTypes !== []) {
63
            foreach ($pageTypes as $pageType) {
64
                if (!is_array($pageType) || !isset($pageType[1]) || $pageType[1] === '--div--') {
65
                    // Skip non arrays and divider items
66
                    continue;
67
                }
68
                [$label, $value] = $pageType;
69
                $icon = $this->iconFactory->mapRecordTypeToIconIdentifier('pages', ['doktype' => $pageType[1]]);
70
                $fieldDefinition['items'][] = [$label, $value, $icon];
71
            }
72
        }
73
    }
74
75
    public function populateAvailableGroupModules(array &$fieldDefinition): void
76
    {
77
        $fieldDefinition['items'] = $this->getAvailableModules('group', $fieldDefinition['items']);
78
    }
79
80
    public function populateAvailableUserModules(array &$fieldDefinition): void
81
    {
82
        $fieldDefinition['items'] = $this->getAvailableModules('user', $fieldDefinition['items']);
83
    }
84
85
    public function populateExcludeFields(array &$fieldDefinition): void
86
    {
87
        $languageService = $this->getLanguageService();
88
        foreach ($this->getGroupedExcludeFields() as $excludeFieldGroup) {
89
            $table = $excludeFieldGroup['table'] ?? '';
90
            $origin = $excludeFieldGroup['origin'] ?? '';
91
            // If the field comes from a FlexForm, the syntax is more complex
92
            if ($origin === 'flexForm') {
93
                // The field comes from a plugins FlexForm
94
                // Add header if not yet set for plugin section
95
                $sectionHeader = $excludeFieldGroup['sectionHeader'] ?? '';
96
                if (!isset($fieldDefinition['items'][$sectionHeader])) {
97
                    // there is no icon handling for plugins - we take the icon from the table
98
                    $icon = $this->iconFactory->mapRecordTypeToIconIdentifier($table, []);
99
                    $fieldDefinition['items'][$sectionHeader] = [$sectionHeader, '--div--', $icon];
100
                }
101
            } elseif (!isset($fieldDefinition['items'][$table])) {
102
                // Add header if not yet set for table
103
                $sectionHeader = $GLOBALS['TCA'][$table]['ctrl']['title'] ?? '';
104
                $icon = $this->iconFactory->mapRecordTypeToIconIdentifier($table, []);
105
                $fieldDefinition['items'][$table] = [$sectionHeader, '--div--', $icon];
106
            }
107
            $fullField = $excludeFieldGroup['fullField'] ?? '';
108
            $fieldName = $excludeFieldGroup['fieldName'] ?? '';
109
            $label = $origin === 'flexForm'
110
                ? ($excludeFieldGroup['fieldLabel'] ?? '')
111
                : $languageService->sL($GLOBALS['TCA'][$table]['columns'][$fieldName]['label'] ?? '');
112
            // Add help text
113
            $languageService->loadSingleTableDescription($table);
114
            $helpText = (string)($GLOBALS['TCA_DESCR'][$table]['columns'][$fullField]['description'] ?? '');
115
            // Item configuration:
116
            $fieldDefinition['items'][] = [
117
                rtrim($label, ':') . ' (' . $fieldName . ')',
118
                $table . ':' . $fullField,
119
                'empty-empty',
120
                null,
121
                $helpText
122
            ];
123
        }
124
    }
125
126
    public function populateExplicitAuthValues(array &$fieldDefinition): void
127
    {
128
        $icons = [
129
            'ALLOW' => 'status-status-permission-granted',
130
            'DENY' => 'status-status-permission-denied'
131
        ];
132
        // Traverse grouped field values:
133
        foreach ($this->getGroupedExplicitAuthFieldValues() as $groupKey => $tableFields) {
134
            if (empty($tableFields['items']) || !is_array($tableFields['items'])) {
135
                continue;
136
            }
137
            // Add header:
138
            $fieldDefinition['items'][] = [
139
                $tableFields['tableFieldLabel'] ?? '',
140
                '--div--',
141
            ];
142
            // Traverse options for this field:
143
            foreach ($tableFields['items'] as $itemValue => $itemContent) {
144
                [$allowDenyMode, $itemLabel, $allowDenyModeLabel] = $itemContent;
145
                // Add item to be selected:
146
                $fieldDefinition['items'][] = [
147
                    '[' . $allowDenyModeLabel . '] ' . $itemLabel,
148
                    $groupKey . ':' . preg_replace('/[:|,]/', '', $itemValue) . ':' . $allowDenyMode,
149
                    $icons[$allowDenyMode]
150
                ];
151
            }
152
        }
153
    }
154
155
    public function populateCustomPermissionOptions(array &$fieldDefinition): void
156
    {
157
        $customOptions = $GLOBALS['TYPO3_CONF_VARS']['BE']['customPermOptions'] ?? [];
158
        if (!is_array($customOptions) || $customOptions === []) {
159
            return;
160
        }
161
        $languageService = $this->getLanguageService();
162
        foreach ($customOptions as $customOptionsKey => $customOptionsValue) {
163
            if (empty($customOptionsValue['items']) || !is_array($customOptionsValue['items'])) {
164
                continue;
165
            }
166
            // Add header:
167
            $fieldDefinition['items'][] = [
168
                $languageService->sL($customOptionsValue['header'] ?? ''),
169
                '--div--'
170
            ];
171
            // Traverse items:
172
            foreach ($customOptionsValue['items'] as $itemKey => $itemConfig) {
173
                $icon = 'empty-empty';
174
                $helpText = '';
175
                if (!empty($itemConfig[1]) && $this->iconRegistry->isRegistered($itemConfig[1])) {
176
                    // Use icon identifier when registered
177
                    $icon = $itemConfig[1];
178
                }
179
                if (!empty($itemConfig[2])) {
180
                    $helpText = $languageService->sL($itemConfig[2]);
181
                }
182
                $fieldDefinition['items'][] = [
183
                    $languageService->sL($itemConfig[0] ?? ''),
184
                    $customOptionsKey . ':' . preg_replace('/[:|,]/', '', $itemKey),
185
                    $icon,
186
                    null,
187
                    $helpText
188
                ];
189
            }
190
        }
191
    }
192
193
    /**
194
     * Get all available modules for the given context: "user" or "group"
195
     *
196
     * @param string $context
197
     * @param array $items
198
     * @return array
199
     */
200
    protected function getAvailableModules(string $context, array $items): array
201
    {
202
        if (!in_array($context, ['user', 'group'], true)) {
203
            return $items;
204
        }
205
        $languageService = $this->getLanguageService();
206
        $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
207
        $moduleLoader->load($GLOBALS['TBE_MODULES']);
208
        $moduleList = $context === 'user' ? $moduleLoader->modListUser : $moduleLoader->modListGroup;
209
        if (!is_array($moduleList) || $moduleList === []) {
210
            return $items;
211
        }
212
        foreach ($moduleList as $module) {
213
            $moduleLabels = $moduleLoader->getLabelsForModule($module);
214
            $moduleArray = GeneralUtility::trimExplode('_', $module, true);
215
            $mainModule = $moduleArray[0] ?? '';
216
            $subModule = $moduleArray[1] ?? '';
217
            // Icon:
218
            if (!empty($subModule)) {
219
                $icon = $moduleLoader->modules[$mainModule]['sub'][$subModule]['iconIdentifier'] ?? '';
220
            } else {
221
                $icon = $moduleLoader->modules[$module]['iconIdentifier'] ?? '';
222
            }
223
            // Add help text
224
            $helpText = [
225
                'title' => $languageService->sL($moduleLabels['shortdescription'] ?? ''),
226
                'description' => $languageService->sL($moduleLabels['description'] ?? '')
227
            ];
228
            $label = '';
229
            // Add label for main module if this is a submodule
230
            if (!empty($subModule)) {
231
                $mainModuleLabels = $moduleLoader->getLabelsForModule($mainModule);
232
                $label .= $languageService->sL($mainModuleLabels['title'] ?? '') . '>';
233
            }
234
            // Add modules own label now
235
            $label .= $languageService->sL($moduleLabels['title'] ?? '');
236
            // Item configuration
237
            $items[] = [$label, $module, $icon, null, $helpText];
238
        }
239
        return $items;
240
    }
241
242
    /**
243
     * Returns an array with the exclude fields as defined in TCA and FlexForms
244
     * Used for listing the exclude fields in be_groups forms.
245
     *
246
     * @return array Array of arrays with excludeFields (fieldName, table:fieldName) from TCA
247
     *               and FlexForms (fieldName, table:extKey;sheetName;fieldName)
248
     */
249
    protected function getGroupedExcludeFields(): array
250
    {
251
        $languageService = $this->getLanguageService();
252
        $excludeFieldGroups = [];
253
254
        // Fetch translations for table names
255
        $tableToTranslation = [];
256
        // All TCA keys
257
        foreach ($GLOBALS['TCA'] as $table => $conf) {
258
            $tableToTranslation[$table] = $languageService->sL($conf['ctrl']['title'] ?? '');
259
        }
260
        // Sort by translations
261
        asort($tableToTranslation);
262
        foreach ($tableToTranslation as $table => $translatedTable) {
263
            $excludeFieldGroup = [];
264
265
            // All field names configured and not restricted to admins
266
            if (!empty($GLOBALS['TCA'][$table]['columns'])
267
                && is_array($GLOBALS['TCA'][$table]['columns'])
268
                && empty($GLOBALS['TCA'][$table]['ctrl']['adminOnly'])
269
                && (empty($GLOBALS['TCA'][$table]['ctrl']['rootLevel']) || !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']))
270
            ) {
271
                foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldDefinition) {
272
                    // Only show fields that can be excluded for editors, or are hidden for non-admins
273
                    if (($fieldDefinition['exclude'] ?? false) && ($fieldDefinition['displayCond'] ?? '') !== 'HIDE_FOR_NON_ADMINS') {
274
                        // Get human readable names of fields
275
                        $translatedField = $languageService->sL($fieldDefinition['label'] ?? '');
276
                        // Add entry, key 'labels' needed for sorting
277
                        $excludeFieldGroup[] = [
278
                            'labels' => $translatedTable . ':' . $translatedField,
279
                            'sectionHeader' => $translatedTable,
280
                            'table' => $table,
281
                            'tableField' => $fieldName,
282
                            'fieldName' => $fieldName,
283
                            'fullField' => $fieldName,
284
                            'fieldLabel' => $translatedField,
285
                            'origin' => 'tca',
286
                        ];
287
                    }
288
                }
289
            }
290
            // All FlexForm fields
291
            $flexFormArray = $this->getRegisteredFlexForms((string)$table);
292
            foreach ($flexFormArray as $tableField => $flexForms) {
293
                // Prefix for field label, e.g. "Plugin Options:"
294
                $labelPrefix = '';
295
                if (!empty($GLOBALS['TCA'][$table]['columns'][$tableField]['label'])) {
296
                    $labelPrefix = $languageService->sL($GLOBALS['TCA'][$table]['columns'][$tableField]['label']);
297
                }
298
                // Get all sheets
299
                foreach ($flexForms as $extIdent => $extConf) {
300
                    if (empty($extConf['sheets']) || !is_array($extConf['sheets'])) {
301
                        continue;
302
                    }
303
                    // Get all fields in sheet
304
                    foreach ($extConf['sheets'] as $sheetName => $sheet) {
305
                        if (empty($sheet['ROOT']['el']) || !is_array($sheet['ROOT']['el'])) {
306
                            continue;
307
                        }
308
                        foreach ($sheet['ROOT']['el'] as $pluginFieldName => $field) {
309
                            // Use only fields that have exclude flag set
310
                            if (empty($field['TCEforms']['exclude'])) {
311
                                continue;
312
                            }
313
                            $fieldLabel = !empty($field['TCEforms']['label']) ? $languageService->sL($field['TCEforms']['label']) : $pluginFieldName;
314
                            $excludeFieldGroup[] = [
315
                                'labels' => trim($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent, ': ') . ':' . $fieldLabel,
316
                                'sectionHeader' => trim($translatedTable . ' ' . $labelPrefix . ' ' . $extIdent, ':'),
317
                                'table' => $table,
318
                                'tableField' => $tableField,
319
                                'extIdent' => $extIdent,
320
                                'fieldName' => $pluginFieldName,
321
                                'fullField' => $tableField . ';' . $extIdent . ';' . $sheetName . ';' . $pluginFieldName,
322
                                'fieldLabel' => $fieldLabel,
323
                                'origin' => 'flexForm',
324
                            ];
325
                        }
326
                    }
327
                }
328
            }
329
            // Sort fields by the translated value
330
            if (!empty($excludeFieldGroup)) {
331
                usort($excludeFieldGroup, static function (array $array1, array $array2) {
332
                    $array1 = reset($array1);
333
                    $array2 = reset($array2);
334
                    if (is_string($array1) && is_string($array2)) {
335
                        return strcasecmp($array1, $array2);
336
                    }
337
                    return 0;
338
                });
339
                $excludeFieldGroups = array_merge($excludeFieldGroups, $excludeFieldGroup);
340
            }
341
        }
342
343
        return $excludeFieldGroups;
344
    }
345
346
    /**
347
     * Returns FlexForm data structures it finds. Used in select "special" for be_groups
348
     * to set "exclude" flags for single flex form fields.
349
     *
350
     * This only finds flex forms registered in 'ds' config sections.
351
     * This does not resolve other sophisticated flex form data structure references.
352
     *
353
     * @todo: This approach is limited and doesn't find everything. It works for casual tt_content plugins, though:
354
     * @todo: The data structure identifier determination depends on data row, but we don't have all rows at hand here.
355
     * @todo: The code thus "guesses" some standard data structure identifier scenarios and tries to resolve those.
356
     * @todo: This guessing can not be solved in a good way. A general registry of "all" possible data structures is
357
     * @todo: probably not wanted, since that wouldn't work for truly dynamic DS calculations. Probably the only
358
     * @todo: thing we could do here is a hook to allow extensions declaring specific data structures to
359
     * @todo: allow backend admins to set exclude flags for certain fields in those cases.
360
     *
361
     * @param string $table Table to handle
362
     * @return array Data structures
363
     */
364
    protected function getRegisteredFlexForms(string $table): array
365
    {
366
        if (empty($GLOBALS['TCA'][$table]['columns']) || !is_array($GLOBALS['TCA'][$table]['columns'])) {
367
            return [];
368
        }
369
        $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
370
        $flexForms = [];
371
        foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $fieldDefinition) {
372
            if (($fieldDefinition['config']['type'] ?? '') !== 'flex'
373
                || empty($fieldDefinition['config']['ds'])
374
                || !is_array($fieldDefinition['config']['ds'])
375
            ) {
376
                continue;
377
            }
378
            $flexForms[$field] = [];
379
            foreach (array_keys($fieldDefinition['config']['ds']) as $flexFormKey) {
380
                $flexFormKey = (string)$flexFormKey;
381
                // Get extension identifier (uses second value if it's not empty, "list" or "*", else first one)
382
                $identFields = GeneralUtility::trimExplode(',', $flexFormKey);
383
                $extIdent = $identFields[0] ?? '';
384
                if (!empty($identFields[1]) && $identFields[1] !== 'list' && $identFields[1] !== '*') {
385
                    $extIdent = $identFields[1];
386
                }
387
                $flexFormDataStructureIdentifier = json_encode([
388
                    'type' => 'tca',
389
                    'tableName' => $table,
390
                    'fieldName' => $field,
391
                    'dataStructureKey' => $flexFormKey,
392
                ]);
393
                try {
394
                    $dataStructure = $flexFormTools->parseDataStructureByIdentifier($flexFormDataStructureIdentifier);
395
                    $flexForms[$field][$extIdent] = $dataStructure;
396
                } catch (InvalidIdentifierException $e) {
397
                    // Deliberately empty: The DS identifier is guesswork and the flex ds parser throws
398
                    // this exception if it can not resolve to a valid data structure. This is "ok" here
399
                    // and the exception is just eaten.
400
                }
401
            }
402
        }
403
        return $flexForms;
404
    }
405
406
    /**
407
     * Returns an array with explicit Allow/Deny fields.
408
     * Used for listing these field/value pairs in be_groups forms
409
     *
410
     * @return array Array with information from all of $GLOBALS['TCA']
411
     */
412
    protected function getGroupedExplicitAuthFieldValues(): array
413
    {
414
        $languageService = $this->getLanguageService();
415
        $allowDenyLabels = [
416
            'ALLOW' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.allow'),
417
            'DENY' => $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.deny')
418
        ];
419
        $allowDenyOptions = [];
420
        foreach ($GLOBALS['TCA'] as $table => $tableConfiguration) {
421
            if (empty($tableConfiguration['columns']) || !is_array($tableConfiguration['columns'])) {
422
                continue;
423
            }
424
            // All field names configured:
425
            foreach ($tableConfiguration['columns'] as $field => $fieldDefinition) {
426
                $fieldConfig = $fieldDefinition['config'] ?? [];
427
                if (($fieldConfig['type'] ?? '') !== 'select' || !(bool)($fieldConfig['authMode'] ?? false)) {
428
                    continue;
429
                }
430
                // Check for items
431
                if (empty($fieldConfig['items']) || !is_array($fieldConfig['items'])) {
432
                    continue;
433
                }
434
                // Get Human Readable names of fields and table:
435
                $allowDenyOptions[$table . ':' . $field]['tableFieldLabel'] =
436
                    $languageService->sL($GLOBALS['TCA'][$table]['ctrl']['title'] ?? '') . ': '
437
                    . $languageService->sL($GLOBALS['TCA'][$table]['columns'][$field]['label'] ?? '');
438
439
                foreach ($fieldConfig['items'] as $item) {
440
                    $itemIdentifier = (string)($item[1] ?? '');
441
                    // Values '' and '--div--' are not controlled by this setting.
442
                    if ($itemIdentifier === '' || $itemIdentifier === '--div--') {
443
                        continue;
444
                    }
445
                    // Find allowDenyMode
446
                    $allowDenyMode = '';
447
                    switch ((string)$fieldConfig['authMode']) {
448
                        case 'explicitAllow':
449
                            $allowDenyMode = 'ALLOW';
450
                            break;
451
                        case 'explicitDeny':
452
                            $allowDenyMode = 'DENY';
453
                            break;
454
                        case 'individual':
455
                            if ($item[4] ?? false) {
456
                                if ($item[4] === 'EXPL_ALLOW') {
457
                                    $allowDenyMode = 'ALLOW';
458
                                } elseif ($item[4] === 'EXPL_DENY') {
459
                                    $allowDenyMode = 'DENY';
460
                                }
461
                            }
462
                            break;
463
                    }
464
                    // Set allowDenyMode
465
                    if ($allowDenyMode) {
466
                        $allowDenyOptions[$table . ':' . $field]['items'][$itemIdentifier] = [
467
                            $allowDenyMode,
468
                            $languageService->sL($item[0] ?? ''),
469
                            $allowDenyLabels[$allowDenyMode]
470
                        ];
471
                    }
472
                }
473
            }
474
        }
475
        return $allowDenyOptions;
476
    }
477
478
    protected function getLanguageService(): LanguageService
479
    {
480
        return $GLOBALS['LANG'];
481
    }
482
}
483