BackendModuleRepository::createEntryFromRawData()   F
last analyzed

Complexity

Conditions 26
Paths 4608

Size

Total Lines 44
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 30
dl 0
loc 44
rs 0
c 0
b 0
f 0
cc 26
nc 4608
nop 1

How to fix   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\Domain\Repository\Module;
17
18
use TYPO3\CMS\Backend\Domain\Model\Module\BackendModule;
19
use TYPO3\CMS\Backend\Module\ModuleLoader;
20
use TYPO3\CMS\Backend\Module\ModuleStorage;
21
use TYPO3\CMS\Backend\Routing\UriBuilder;
22
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
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\SingletonInterface;
27
use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29
/**
30
 * Repository for backend module menu
31
 * compiles all data from $GLOBALS[TBE_MODULES]
32
 */
33
class BackendModuleRepository implements SingletonInterface
34
{
35
    /**
36
     * @var \TYPO3\CMS\Backend\Module\ModuleStorage
37
     */
38
    protected $moduleStorage;
39
40
    /**
41
     * Constructs the module menu and gets the Singleton instance of the menu
42
     */
43
    public function __construct()
44
    {
45
        $this->moduleStorage = GeneralUtility::makeInstance(ModuleStorage::class);
46
47
        $rawData = $this->getRawModuleMenuData();
48
49
        $this->convertRawModuleDataToModuleMenuObject($rawData);
50
        $this->createMenuEntriesForTbeModulesExt();
51
    }
52
53
    /**
54
     * loads all module information in the module storage
55
     *
56
     * @param array $excludeGroupNames
57
     * @return \SplObjectStorage
58
     */
59
    public function loadAllowedModules(array $excludeGroupNames = [])
60
    {
61
        if (empty($excludeGroupNames)) {
62
            return $this->moduleStorage->getEntries();
63
        }
64
65
        $modules = new \SplObjectStorage();
66
        foreach ($this->moduleStorage->getEntries() as $moduleGroup) {
67
            if (!in_array($moduleGroup->getName(), $excludeGroupNames, true)) {
68
                if ($moduleGroup->getChildren()->count() > 0 || $moduleGroup->isStandalone()) {
69
                    $modules->attach($moduleGroup);
70
                }
71
            }
72
        }
73
74
        return $modules;
75
    }
76
77
    /**
78
     * @param string $groupName
79
     * @return \SplObjectStorage|false
80
     **/
81
    public function findByGroupName($groupName = '')
82
    {
83
        foreach ($this->moduleStorage->getEntries() as $moduleGroup) {
84
            if ($moduleGroup->getName() === $groupName) {
85
                return $moduleGroup;
86
            }
87
        }
88
89
        return false;
90
    }
91
92
    /**
93
     * Finds a module menu entry by name
94
     *
95
     * @param string $name
96
     * @return \TYPO3\CMS\Backend\Domain\Model\Module\BackendModule|false
97
     */
98
    public function findByModuleName($name)
99
    {
100
        $entries = $this->moduleStorage->getEntries();
101
        $entry = $this->findByModuleNameInGivenEntries($name, $entries);
102
        return $entry;
103
    }
104
105
    /**
106
     * Finds a module menu entry by name in a given storage
107
     *
108
     * @param string $name
109
     * @param \SplObjectStorage $entries
110
     * @return \TYPO3\CMS\Backend\Domain\Model\Module\BackendModule|false
111
     */
112
    public function findByModuleNameInGivenEntries($name, \SplObjectStorage $entries)
113
    {
114
        foreach ($entries as $entry) {
115
            if ($entry->getName() === $name) {
116
                return $entry;
117
            }
118
            $children = $entry->getChildren();
119
            if (!empty($children)) {
120
                $childRecord = $this->findByModuleNameInGivenEntries($name, $children);
121
                if ($childRecord !== false) {
122
                    return $childRecord;
123
                }
124
            }
125
        }
126
        return false;
127
    }
128
129
    /**
130
     * Creates the module menu object structure from the raw data array
131
     *
132
     * @param array $rawModuleData
133
     */
134
    protected function convertRawModuleDataToModuleMenuObject(array $rawModuleData)
135
    {
136
        foreach ($rawModuleData as $module) {
137
            $entry = $this->createEntryFromRawData($module);
138
            if (isset($module['subitems']) && !empty($module['subitems'])) {
139
                foreach ($module['subitems'] as $subitem) {
140
                    $subEntry = $this->createEntryFromRawData($subitem);
141
                    $entry->addChild($subEntry);
142
                }
143
            }
144
            $this->moduleStorage->attachEntry($entry);
145
        }
146
    }
147
148
    /**
149
     * Creates a menu entry object from an array
150
     *
151
     * @param array $module
152
     * @return \TYPO3\CMS\Backend\Domain\Model\Module\BackendModule
153
     */
154
    protected function createEntryFromRawData(array $module)
155
    {
156
        /** @var \TYPO3\CMS\Backend\Domain\Model\Module\BackendModule $entry */
157
        $entry = GeneralUtility::makeInstance(BackendModule::class);
158
        if (!empty($module['name']) && is_string($module['name'])) {
159
            $entry->setName($module['name']);
160
        }
161
        if (!empty($module['title']) && is_string($module['title'])) {
162
            $entry->setTitle($this->getLanguageService()->sL($module['title']));
163
        }
164
        if (!empty($module['onclick']) && is_string($module['onclick'])) {
165
            $entry->setOnClick($module['onclick']);
166
        }
167
        if (!empty($module['link']) && is_string($module['link'])) {
168
            $entry->setLink($module['link']);
169
        } elseif (empty($module['link']) && !empty($module['path']) && is_string($module['path'])) {
170
            $entry->setLink($module['path']);
171
        }
172
        if (!empty($module['component']) && is_string($module['component'])) {
173
            $entry->setComponent($module['component']);
174
        }
175
        if (!empty($module['description']) && is_string($module['description'])) {
176
            $entry->setDescription($this->getLanguageService()->sL($module['description']));
177
        }
178
        if (!empty($module['icon'])) {
179
            $entry->setIcon($module['icon']);
180
        }
181
        if (!empty($module['navigationComponentId']) && is_string($module['navigationComponentId'])) {
182
            $entry->setNavigationComponentId($module['navigationComponentId']);
183
        }
184
        if (!empty($module['navigationFrameScript']) && is_string($module['navigationFrameScript'])) {
185
            $entry->setNavigationFrameScript($module['navigationFrameScript']);
186
        } elseif (!empty($module['parentNavigationFrameScript']) && is_string($module['parentNavigationFrameScript'])) {
187
            $entry->setNavigationFrameScript($module['parentNavigationFrameScript']);
188
        }
189
        if (!empty($module['navigationFrameScriptParam']) && is_string($module['navigationFrameScriptParam'])) {
190
            $entry->setNavigationFrameScriptParameters($module['navigationFrameScriptParam']);
191
        }
192
        if (!empty($module['standalone'])) {
193
            $entry->setStandalone((bool)$module['standalone']);
194
        }
195
        $moduleMenuState = json_decode($this->getBackendUser()->uc['modulemenu'] ?? '{}', true);
196
        $entry->setCollapsed(isset($moduleMenuState[$module['name']]));
197
        return $entry;
198
    }
199
200
    /**
201
     * Creates the "third level" menu entries (submodules for the info module for
202
     * example) from the TBE_MODULES_EXT array
203
     */
204
    protected function createMenuEntriesForTbeModulesExt()
205
    {
206
        foreach ($GLOBALS['TBE_MODULES_EXT'] ?? [] as $mainModule => $tbeModuleExt) {
207
            [$main] = explode('_', $mainModule);
208
            $mainEntry = $this->findByModuleName($main);
209
            if ($mainEntry === false) {
210
                continue;
211
            }
212
213
            $subEntries = $mainEntry->getChildren();
214
            if (empty($subEntries)) {
215
                continue;
216
            }
217
            $matchingSubEntry = $this->findByModuleName($mainModule);
218
            if ($matchingSubEntry !== false) {
219
                if (isset($tbeModuleExt['MOD_MENU']) && isset($tbeModuleExt['MOD_MENU']['function'])) {
220
                    foreach ($tbeModuleExt['MOD_MENU']['function'] as $subModule) {
221
                        $entry = $this->createEntryFromRawData($subModule);
222
                        $matchingSubEntry->addChild($entry);
223
                    }
224
                }
225
            }
226
        }
227
    }
228
229
    /**
230
     * loads the module menu from the moduleloader based on $GLOBALS['TBE_MODULES']
231
     * and compiles an array with all the data needed for menu etc.
232
     *
233
     * @return array
234
     */
235
    public function getRawModuleMenuData()
236
    {
237
        // Loads the backend modules available for the logged in user.
238
        /** @var ModuleLoader $moduleLoader */
239
        $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
240
        $moduleLoader->observeWorkspaces = true;
241
        $moduleLoader->load($GLOBALS['TBE_MODULES']);
242
        $loadedModules = $moduleLoader->modules;
243
244
        $modules = [];
245
246
        // Unset modules that are meant to be hidden from the menu.
247
        $loadedModules = $this->removeHiddenModules($loadedModules);
248
        $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
249
        $dummyScript = (string)$uriBuilder->buildUriFromRoute('dummy');
250
        foreach ($loadedModules as $moduleName => $moduleData) {
251
            $moduleLink = '';
252
            $moduleComponent = '';
253
            if (!is_array($moduleData['sub'] ?? null)) {
254
                $moduleLink = $moduleData['script'];
255
                $moduleComponent = $moduleData['component'] ?? '';
256
            }
257
            $moduleLink = GeneralUtility::resolveBackPath($moduleLink);
258
            $moduleLabels = $moduleLoader->getLabelsForModule($moduleName);
259
            $moduleKey = 'modmenu_' . $moduleName;
260
            $modules[$moduleKey] = [
261
                'name' => $moduleName,
262
                'title' => $moduleLabels['title'],
263
                'icon' => $this->getModuleIcon($moduleKey, $moduleData),
264
                'link' => $moduleLink,
265
                'component' => $moduleComponent,
266
                'description' => $moduleLabels['shortdescription'],
267
                'standalone' => (bool)($moduleData['standalone'] ?? false)
268
            ];
269
            if ((($moduleData['standalone'] ?? false) === false) && !is_array($moduleData['sub'] ?? null) && ($moduleData['script'] ?? null) !== $dummyScript) {
270
                // Work around for modules with own main entry, but being self the only submodule
271
                $modules[$moduleKey]['subitems'][$moduleKey] = [
272
                    'name' => $moduleName,
273
                    'title' => $moduleLabels['title'] ?? '',
274
                    'icon' => $this->getModuleIcon($moduleKey, $moduleData),
275
                    'link' => $moduleLink,
276
                    'component' => $moduleComponent,
277
                    'originalLink' => $moduleLink,
278
                    'description' => $moduleLabels['shortdescription'] ?? '',
279
                    'navigationFrameScript' => null,
280
                    'navigationFrameScriptParam' => null,
281
                    'navigationComponentId' => null
282
                ];
283
            } elseif (is_array($moduleData['sub'] ?? null)) {
284
                foreach ($moduleData['sub'] as $submoduleName => $submoduleData) {
285
                    $submoduleComponent = $submoduleData['component'] ?? '';
286
                    if (isset($submoduleData['script'])) {
287
                        $submoduleLink = GeneralUtility::resolveBackPath($submoduleData['script']);
288
                    } else {
289
                        $submoduleLink = (string)$uriBuilder->buildUriFromRoute($submoduleData['name']);
290
                    }
291
                    $submoduleKey = $moduleName . '_' . $submoduleName;
292
                    $submoduleLabels = $moduleLoader->getLabelsForModule($submoduleKey);
293
                    $submoduleDescription = $submoduleLabels['shortdescription'];
294
                    $originalLink = $submoduleLink;
295
                    $navigationFrameScript = $submoduleData['navFrameScript'] ?? null;
296
                    $modules[$moduleKey]['subitems'][$submoduleKey] = [
297
                        'name' => $moduleName . '_' . $submoduleName,
298
                        'title' => $submoduleLabels['title'],
299
                        'icon' => $this->getModuleIcon($moduleKey, $submoduleData),
300
                        'link' => $submoduleLink,
301
                        'component' => $submoduleComponent,
302
                        'originalLink' => $originalLink,
303
                        'description' => $submoduleDescription,
304
                        'navigationFrameScript' => $navigationFrameScript,
305
                        'navigationFrameScriptParam' => $submoduleData['navFrameScriptParam'] ?? null,
306
                        'navigationComponentId' => $submoduleData['navigationComponentId'] ?? null
307
                    ];
308
                    // if the main module has a navframe script, inherit to the submodule,
309
                    // but only if it is not disabled explicitly (option is set to FALSE)
310
                    if (($moduleData['navFrameScript'] ?? false) && $submoduleData['inheritNavigationComponentFromMainModule'] !== false) {
311
                        $modules[$moduleKey]['subitems'][$submoduleKey]['parentNavigationFrameScript'] = $moduleData['navFrameScript'];
312
                    }
313
                }
314
            }
315
        }
316
        return $modules;
317
    }
318
319
    /**
320
     * @return bool
321
     * @todo deprecate
322
     */
323
    public function modulesHaveNavigationComponent(): bool
324
    {
325
        /** @var BackendModule $module */
326
        foreach ($this->moduleStorage->getEntries() as $module) {
327
            if ($module->getNavigationComponentId() !== '') {
328
                return true;
329
            }
330
        }
331
332
        return false;
333
    }
334
335
    /**
336
     * Reads User configuration from options.hideModules and removes
337
     * modules accordingly.
338
     *
339
     * @param array $loadedModules
340
     * @return array
341
     */
342
    protected function removeHiddenModules($loadedModules)
343
    {
344
        $userTsConfig = $this->getBackendUser()->getTSConfig();
345
346
        // Hide modules if set in userTS.
347
        $hiddenMainModules = GeneralUtility::trimExplode(',', $userTsConfig['options.']['hideModules'] ?? '', true);
348
        foreach ($hiddenMainModules as $hiddenMainModule) {
349
            unset($loadedModules[$hiddenMainModule]);
350
        }
351
352
        // Hide sub-modules if set in userTS.
353
        $hiddenModules = $userTsConfig['options.']['hideModules.'] ?? [];
354
        if (is_array($hiddenModules)) {
355
            foreach ($hiddenModules as $mainModuleName => $subModules) {
356
                $hiddenSubModules = GeneralUtility::trimExplode(',', $subModules, true);
357
                foreach ($hiddenSubModules as $hiddenSubModule) {
358
                    unset($loadedModules[$mainModuleName]['sub'][$hiddenSubModule]);
359
                }
360
            }
361
        }
362
363
        return $loadedModules;
364
    }
365
366
    /**
367
     * gets the module icon
368
     *
369
     * @param string $moduleKey Module key
370
     * @param array $moduleData the compiled data associated with it
371
     * @return string Icon data, either sprite or <img> tag
372
     */
373
    protected function getModuleIcon($moduleKey, $moduleData)
374
    {
375
        $iconIdentifier = !empty($moduleData['iconIdentifier'])
376
            ? $moduleData['iconIdentifier']
377
            : 'module-icon-' . $moduleKey;
378
        $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class);
379
        if ($iconRegistry->isRegistered($iconIdentifier)) {
380
            $iconFactory = GeneralUtility::makeInstance(IconFactory::class);
381
            return $iconFactory->getIcon($iconIdentifier)->render('inline');
382
        }
383
        return '';
384
    }
385
386
    /**
387
     * Return language service instance
388
     *
389
     * @return LanguageService
390
     */
391
    protected function getLanguageService()
392
    {
393
        return $GLOBALS['LANG'];
394
    }
395
396
    /**
397
     * @return BackendUserAuthentication
398
     */
399
    protected function getBackendUser(): BackendUserAuthentication
400
    {
401
        return $GLOBALS['BE_USER'];
402
    }
403
}
404