Passed
Push — master ( 2b355a...25a926 )
by
unknown
13:06
created

InfoModuleController::getExtObjContent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 8
rs 10
c 0
b 0
f 0
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\Info\Controller;
17
18
use Psr\Container\ContainerInterface;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
use TYPO3\CMS\Backend\Routing\PreviewUriBuilder;
22
use TYPO3\CMS\Backend\Routing\UriBuilder;
23
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
24
use TYPO3\CMS\Backend\Template\ModuleTemplate;
25
use TYPO3\CMS\Backend\Utility\BackendUtility;
26
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
27
use TYPO3\CMS\Core\Http\HtmlResponse;
28
use TYPO3\CMS\Core\Imaging\Icon;
29
use TYPO3\CMS\Core\Localization\LanguageService;
30
use TYPO3\CMS\Core\Messaging\FlashMessage;
31
use TYPO3\CMS\Core\Messaging\FlashMessageService;
32
use TYPO3\CMS\Core\Type\Bitmask\Permission;
33
use TYPO3\CMS\Core\Utility\GeneralUtility;
34
use TYPO3\CMS\Fluid\View\StandaloneView;
35
36
/**
37
 * Script Class for the Web > Info module
38
 * This class creates the framework to which other extensions can connect their sub-modules
39
 * @internal This class is a specific Backend controller implementation and is not part of the TYPO3's Core API.
40
 */
41
class InfoModuleController
42
{
43
    /**
44
     * @var array Used by client classes.
45
     */
46
    public $pageinfo;
47
48
    /**
49
     * The name of the module
50
     *
51
     * @var string
52
     */
53
    protected $moduleName = 'web_info';
54
55
    /**
56
     * ModuleTemplate Container
57
     *
58
     * @var ModuleTemplate
59
     */
60
    protected $moduleTemplate;
61
62
    /**
63
     * @var StandaloneView
64
     */
65
    protected $view;
66
67
    /**
68
     * @var UriBuilder
69
     */
70
    protected $uriBuilder;
71
72
    /**
73
     * @var FlashMessageService
74
     */
75
    protected $flashMessageService;
76
77
    /**
78
     * @var ContainerInterface
79
     */
80
    protected $container;
81
82
    /**
83
     * @var int Value of the GET/POST var 'id'
84
     */
85
    protected $id;
86
87
    /**
88
     * A WHERE clause for selection records from the pages table based on read-permissions of the current backend user.
89
     *
90
     * @var string
91
     */
92
    protected $perms_clause;
93
94
    /**
95
     * The module menu items array. Each key represents a key for which values can range between the items in the array of that key.
96
     * Written by client classes.
97
     *
98
     * @var array
99
     */
100
    public $MOD_MENU = [
101
        'function' => []
102
    ];
103
104
    /**
105
     * Current settings for the keys of the MOD_MENU array
106
     * Written by client classes.
107
     *
108
     * @var array
109
     */
110
    public $MOD_SETTINGS = [];
111
112
    /**
113
     * Module TSconfig based on PAGE TSconfig / USER TSconfig
114
     *
115
     * @var array
116
     */
117
    protected $modTSconfig;
118
119
    /**
120
     * If type is 'ses' then the data is stored as session-lasting data. This means that it'll be wiped out the next time the user logs in.
121
     * Can be set from extension classes of this class before the init() function is called.
122
     *
123
     * @var string
124
     */
125
    protected $modMenu_type = '';
126
127
    /**
128
     * dontValidateList can be used to list variables that should not be checked if their value is found in the MOD_MENU array. Used for dynamically generated menus.
129
     * Can be set from extension classes of this class before the init() function is called.
130
     *
131
     * @var string
132
     */
133
    protected $modMenu_dontValidateList = '';
134
135
    /**
136
     * List of default values from $MOD_MENU to set in the output array (only if the values from MOD_MENU are not arrays)
137
     * Can be set from extension classes of this class before the init() function is called.
138
     *
139
     * @var string
140
     */
141
    protected $modMenu_setDefaultList = '';
142
143
    /**
144
     * @var array Contains module configuration parts from TBE_MODULES_EXT if found
145
     */
146
    protected $extClassConf;
147
148
    /**
149
     * Generally used for accumulating the output content of backend modules
150
     *
151
     * @var string
152
     */
153
    protected $content = '';
154
155
    /**
156
     * May contain an instance of a 'Function menu module' which connects to this backend module.
157
     *
158
     * @var object
159
     */
160
    protected $extObj;
161
162
    /**
163
     * Constructor
164
     */
165
    public function __construct(
166
        ModuleTemplate $moduleTemplate,
167
        UriBuilder $uriBuilder,
168
        FlashMessageService $flashMessageService,
169
        ContainerInterface $container
170
    ) {
171
        $this->moduleTemplate = $moduleTemplate;
172
        $this->uriBuilder = $uriBuilder;
173
        $this->flashMessageService = $flashMessageService;
174
        $this->container = $container;
175
176
        $this->getLanguageService()->includeLLFile('EXT:info/Resources/Private/Language/locallang_mod_web_info.xlf');
177
    }
178
179
    /**
180
     * Initializes the backend module by setting internal variables, initializing the menu.
181
     */
182
    protected function init(ServerRequestInterface $request)
183
    {
184
        $this->id = (int)($request->getQueryParams()['id'] ?? $request->getParsedBody()['id'] ?? 0);
185
        $this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
186
        $this->menuConfig($request);
187
        $this->handleExternalFunctionValue();
188
    }
189
190
    /**
191
     * Initialize module header etc and call extObjContent function
192
     *
193
     * @param ServerRequestInterface $request the current request
194
     */
195
    protected function main(ServerRequestInterface $request)
196
    {
197
        $backendUser = $this->getBackendUser();
198
199
        // The page will show only if there is a valid page and if this page
200
        // may be viewed by the user
201
        $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Backend\Utilit...d, $this->perms_clause) can also be of type false. However, the property $pageinfo is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
202
        if ($this->pageinfo) {
203
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
204
        }
205
        $access = is_array($this->pageinfo);
206
        if ($this->id && $access || $backendUser->isAdmin() && !$this->id) {
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($this->id && $access) |...sAdmin() && ! $this->id, Probably Intended Meaning: $this->id && ($access ||...Admin() && ! $this->id)
Loading history...
207
            if ($backendUser->isAdmin() && !$this->id) {
208
                $this->pageinfo = ['title' => '[root-level]', 'uid' => 0, 'pid' => 0];
209
            }
210
            // JavaScript
211
            $this->moduleTemplate->addJavaScriptCode(
212
                'WebFuncInLineJS',
213
                'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
214
				'
215
            );
216
            // Setting up the context sensitive menu:
217
            $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
218
219
            $this->view = $this->getFluidTemplateObject();
220
            $this->view->assign('moduleName', (string)$this->uriBuilder->buildUriFromRoute($this->moduleName));
221
            $this->view->assign('functionMenuModuleContent', $this->extObjContent($request));
222
            // Setting up the buttons and markers for doc header
223
            $this->getButtons($request);
224
            $this->generateMenu();
225
            $this->content = $this->view->render();
226
        } else {
227
            // If no access or if ID == zero
228
            $this->content = $this->moduleTemplate->header($this->getLanguageService()->getLL('title'));
229
        }
230
    }
231
232
    /**
233
     * Injects the request object for the current request or subrequest
234
     * Then checks for module functions that have hooked in, and renders menu etc.
235
     *
236
     * @param ServerRequestInterface $request the current request
237
     * @return ResponseInterface the response with the content
238
     */
239
    public function mainAction(ServerRequestInterface $request): ResponseInterface
240
    {
241
        $this->init($request);
242
243
        // Checking for first level external objects
244
        $this->checkExtObj($request);
245
246
        $this->main($request);
247
248
        $this->moduleTemplate->setContent($this->content);
249
        return new HtmlResponse($this->moduleTemplate->renderContent());
250
    }
251
252
    /**
253
     * Create the panel of buttons for submitting the form or otherwise perform operations.
254
     *
255
     * @param ServerRequestInterface $request the current request
256
     */
257
    protected function getButtons(ServerRequestInterface $request)
258
    {
259
        $languageService = $this->getLanguageService();
260
        $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
261
        // View page
262
        $previewDataAttributes =PreviewUriBuilder::create((int)$this->pageinfo['uid'])
263
            ->withRootLine(BackendUtility::BEgetRootLine($this->pageinfo['uid']))
264
            ->buildDispatcherDataAttributes();
265
        $viewButton = $buttonBar->makeLinkButton()
266
            ->setHref('#')
267
            ->setDataAttributes($previewDataAttributes ?? [])
268
            ->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
269
            ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-page', Icon::SIZE_SMALL));
270
        $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
271
272
        // Shortcut
273
        $shortcutArguments = [
274
            'id' => $request->getQueryParams()['id'] ?? 0,
275
        ];
276
        foreach (array_keys($this->MOD_MENU) as $key) {
277
            if (!empty($this->MOD_SETTINGS[$key])) {
278
                if (!is_array($shortcutArguments['SET'])) {
279
                    $shortcutArguments['SET'] = [];
280
                }
281
                $shortcutArguments['SET'][$key] = $this->MOD_SETTINGS[$key];
282
            }
283
        }
284
        $shortCutButton = $buttonBar->makeShortcutButton()
285
            ->setRouteIdentifier($this->moduleName)
286
            ->setDisplayName($this->MOD_MENU['function'][$this->MOD_SETTINGS['function']])
287
            ->setArguments($shortcutArguments);
288
        $buttonBar->addButton($shortCutButton, ButtonBar::BUTTON_POSITION_RIGHT);
289
290
        // CSH
291
        $cshButton = $buttonBar->makeHelpButton()
292
            ->setModuleName('xMOD_csh_corebe')
293
            ->setFieldName('pagetree_overview');
294
        $buttonBar->addButton($cshButton);
295
    }
296
297
    /**
298
     * Generate the ModuleMenu
299
     */
300
    protected function generateMenu()
301
    {
302
        $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
303
        $menu->setIdentifier('WebInfoJumpMenu');
304
        foreach ($this->MOD_MENU['function'] as $controller => $title) {
305
            $item = $menu
306
                ->makeMenuItem()
307
                ->setHref(
308
                    (string)$this->uriBuilder->buildUriFromRoute(
309
                        $this->moduleName,
310
                        [
311
                            'id' => $this->id,
312
                            'SET' => [
313
                                'function' => $controller
314
                            ]
315
                        ]
316
                    )
317
                )
318
                ->setTitle($title);
319
            if ($controller === $this->MOD_SETTINGS['function']) {
320
                $item->setActive(true);
321
            }
322
            $menu->addMenuItem($item);
323
        }
324
        $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
325
    }
326
327
    /**
328
     * returns a new standalone view, shorthand function
329
     *
330
     * @return StandaloneView
331
     */
332
    protected function getFluidTemplateObject()
333
    {
334
        $view = GeneralUtility::makeInstance(StandaloneView::class);
335
        $view->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Layouts')]);
336
        $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Partials')]);
337
        $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Templates')]);
338
339
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Templates/Main.html'));
340
341
        $view->getRequest()->setControllerExtensionName('info');
342
        return $view;
343
    }
344
345
    /**
346
     * Initializes the internal MOD_MENU array setting and unsetting items based on various conditions. It also merges in external menu items from the global array TBE_MODULES_EXT (see mergeExternalItems())
347
     * Then MOD_SETTINGS array is cleaned up (see \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()) so it contains only valid values. It's also updated with any SET[] values submitted.
348
     * Also loads the modTSconfig internal variable.
349
     */
350
    protected function menuConfig(ServerRequestInterface $request)
351
    {
352
        // Page / user TSconfig settings and blinding of menu-items
353
        $this->modTSconfig['properties'] = BackendUtility::getPagesTSconfig($this->id)['mod.']['web_info.'] ?? [];
354
        $this->MOD_MENU['function'] = $this->mergeExternalItems('web_info', 'function', $this->MOD_MENU['function']);
355
        $blindActions = $this->modTSconfig['properties']['menu.']['function.'] ?? [];
356
        foreach ($blindActions as $key => $value) {
357
            if (!$value && array_key_exists($key, $this->MOD_MENU['function'])) {
358
                unset($this->MOD_MENU['function'][$key]);
359
            }
360
        }
361
        $moduleSet = $request->getParsedBody()['SET'] ?? $request->getQueryParams()['SET'] ?? [];
362
        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $moduleSet, 'web_info', $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
363
    }
364
365
    /**
366
     * Merges menu items from global array $TBE_MODULES_EXT
367
     *
368
     * @param string $modName Module name for which to find value
369
     * @param string $menuKey Menu key, eg. 'function' for the function menu.
370
     * @param array $menuArr The part of a MOD_MENU array to work on.
371
     * @return array Modified array part.
372
     * @internal
373
     */
374
    protected function mergeExternalItems($modName, $menuKey, $menuArr)
375
    {
376
        $mergeArray = $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
377
        if (is_array($mergeArray)) {
378
            $backendUser = $this->getBackendUser();
379
            foreach ($mergeArray as $k => $v) {
380
                if (((string)$v['ws'] === '' || ($backendUser->workspace === 0 && GeneralUtility::inList($v['ws'], 'online')))
381
                    || ($backendUser->workspace > 0 && GeneralUtility::inList($v['ws'], 'custom'))
382
                ) {
383
                    $menuArr[$k] = $this->getLanguageService()->sL($v['title']);
384
                }
385
            }
386
        }
387
        return $menuArr;
388
    }
389
390
    /**
391
     * Loads $this->extClassConf with the configuration for the CURRENT function of the menu.
392
     *
393
     * @param string $MM_key The key to MOD_MENU for which to fetch configuration. 'function' is default since it is first and foremost used to get information per "extension object" (I think that is what its called)
394
     * @param string $MS_value The value-key to fetch from the config array. If NULL (default) MOD_SETTINGS[$MM_key] will be used. This is useful if you want to force another function than the one defined in MOD_SETTINGS[function]. Call this in init() function of your Script Class: handleExternalFunctionValue('function', $forcedSubModKey)
395
     */
396
    protected function handleExternalFunctionValue($MM_key = 'function', $MS_value = null)
397
    {
398
        if ($MS_value === null) {
399
            $MS_value = $this->MOD_SETTINGS[$MM_key];
400
        }
401
        $this->extClassConf = $this->getExternalItemConfig('web_info', $MM_key, $MS_value);
402
    }
403
404
    /**
405
     * Returns configuration values from the global variable $TBE_MODULES_EXT for the module given.
406
     * For example if the module is named "web_info" and the "function" key ($menuKey) of MOD_SETTINGS is "stat" ($value) then you will have the values
407
     * of $TBE_MODULES_EXT['webinfo']['MOD_MENU']['function']['stat'] returned.
408
     *
409
     * @param string $modName Module name
410
     * @param string $menuKey Menu key, eg. "function" for the function menu. See $this->MOD_MENU
411
     * @param string $value Optionally the value-key to fetch from the array that would otherwise have been returned if this value was not set. Look source...
412
     * @return mixed The value from the TBE_MODULES_EXT array.
413
     */
414
    protected function getExternalItemConfig($modName, $menuKey, $value = '')
415
    {
416
        if (isset($GLOBALS['TBE_MODULES_EXT'][$modName])) {
417
            return (string)$value !== ''
418
                ? $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey][$value]
419
                : $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
420
        }
421
        return null;
422
    }
423
424
    /**
425
     * Creates an instance of the class found in $this->extClassConf['name'] in $this->extObj if any (this should hold three keys, "name", "path" and "title" if a "Function menu module" tries to connect...)
426
     * This value in extClassConf might be set by an extension (in an ext_tables/ext_localconf file) which thus "connects" to a module.
427
     * The array $this->extClassConf is set in handleExternalFunctionValue() based on the value of MOD_SETTINGS[function]
428
     * If an instance is created it is initiated with $this passed as value and $this->extClassConf as second argument. Further the $this->MOD_SETTING is cleaned up again after calling the init function.
429
     */
430
    protected function checkExtObj(ServerRequestInterface $request)
431
    {
432
        if (is_array($this->extClassConf) && $this->extClassConf['name']) {
433
            if ($this->container->has($this->extClassConf['name'])) {
434
                $this->extObj = $this->container->get($this->extClassConf['name']);
435
            } else {
436
                $this->extObj = GeneralUtility::makeInstance($this->extClassConf['name']);
437
            }
438
            if (is_callable([$this->extObj, 'init'])) {
439
                $this->extObj->init($this, $request);
440
            }
441
            // Re-write:
442
            $moduleSet = $request->getParsedBody()['SET'] ?? $request->getQueryParams()['SET'] ?? [];
443
            $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $moduleSet, 'web_info', $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
444
        }
445
    }
446
447
    /**
448
     * Calls the 'main' function inside the "Function menu module" if present
449
     */
450
    protected function extObjContent(ServerRequestInterface $request)
451
    {
452
        if ($this->extObj === null) {
453
            $languageService = $this->getLanguageService();
454
            $flashMessage = GeneralUtility::makeInstance(
455
                FlashMessage::class,
456
                $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:no_modules_registered'),
457
                $languageService->getLL('title'),
458
                FlashMessage::ERROR
459
            );
460
            /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
461
            $defaultFlashMessageQueue = $this->flashMessageService->getMessageQueueByIdentifier();
462
            $defaultFlashMessageQueue->enqueue($flashMessage);
463
        } else {
464
            if (is_callable([$this->extObj, 'main'])) {
465
                $main = $this->extObj->main($request);
466
                if ($main instanceof ResponseInterface) {
467
                    $stream = $main->getBody();
468
                    $stream->rewind();
469
                    $main = $stream->getContents();
470
                }
471
                return $main;
472
            }
473
        }
474
    }
475
476
    /**
477
     * @return LanguageService
478
     */
479
    protected function getLanguageService(): LanguageService
480
    {
481
        return $GLOBALS['LANG'];
482
    }
483
484
    /**
485
     * @return BackendUserAuthentication
486
     */
487
    protected function getBackendUser(): BackendUserAuthentication
488
    {
489
        return $GLOBALS['BE_USER'];
490
    }
491
}
492