Completed
Push — master ( e40022...db61a9 )
by
unknown
14:02
created

PageLayoutController::renderContent()   F

Complexity

Conditions 15
Paths 540

Size

Total Lines 103
Code Lines 66

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
eloc 66
c 0
b 0
f 0
nc 540
nop 0
dl 0
loc 103
rs 2.3887

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\Backend\Controller;
19
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use TYPO3\CMS\Backend\Module\ModuleLoader;
23
use TYPO3\CMS\Backend\Routing\UriBuilder;
24
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
25
use TYPO3\CMS\Backend\Template\ModuleTemplate;
26
use TYPO3\CMS\Backend\Utility\BackendUtility;
27
use TYPO3\CMS\Backend\View\BackendLayoutView;
28
use TYPO3\CMS\Backend\View\PageLayoutContext;
29
use TYPO3\CMS\Backend\View\PageLayoutView;
30
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
31
use TYPO3\CMS\Core\Configuration\Features;
32
use TYPO3\CMS\Core\Database\ConnectionPool;
33
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
34
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
35
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
36
use TYPO3\CMS\Core\Http\HtmlResponse;
37
use TYPO3\CMS\Core\Imaging\Icon;
38
use TYPO3\CMS\Core\Imaging\IconFactory;
39
use TYPO3\CMS\Core\Localization\LanguageService;
40
use TYPO3\CMS\Core\Page\PageRenderer;
41
use TYPO3\CMS\Core\Site\Entity\SiteInterface;
42
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
43
use TYPO3\CMS\Core\Type\Bitmask\Permission;
44
use TYPO3\CMS\Core\Utility\GeneralUtility;
45
use TYPO3\CMS\Core\Versioning\VersionState;
46
use TYPO3\CMS\Fluid\View\StandaloneView;
47
use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
48
49
/**
50
 * Script Class for Web > Layout module
51
 */
52
class PageLayoutController
53
{
54
    /**
55
     * Page Id for which to make the listing
56
     *
57
     * @var int
58
     * @internal
59
     */
60
    public $id;
61
62
    /**
63
     * Module TSconfig
64
     *
65
     * @var array
66
     */
67
    protected $modTSconfig = [];
68
69
    /**
70
     * Module shared TSconfig
71
     *
72
     * @var array
73
     */
74
    protected $modSharedTSconfig = [];
75
76
    /**
77
     * Current ids page record
78
     *
79
     * @var array|bool
80
     * @internal
81
     */
82
    public $pageinfo;
83
84
    /**
85
     * List of column-integers to edit. Is set from TSconfig, default is "1,0,2,3"
86
     *
87
     * @var string
88
     */
89
    protected $colPosList;
90
91
    /**
92
     * Currently selected language for editing content elements
93
     *
94
     * @var int
95
     */
96
    protected $current_sys_language;
97
98
    /**
99
     * Menu configuration
100
     *
101
     * @var array
102
     */
103
    protected $MOD_MENU = [];
104
105
    /**
106
     * Module settings (session variable)
107
     *
108
     * @var array
109
     * @internal
110
     */
111
    public $MOD_SETTINGS = [];
112
113
    /**
114
     * List of column-integers accessible to the current BE user.
115
     * Is set from TSconfig, default is $colPosList
116
     *
117
     * @var string
118
     */
119
    protected $activeColPosList;
120
121
    /**
122
     * @var IconFactory
123
     */
124
    protected $iconFactory;
125
126
    /**
127
     * The name of the module
128
     *
129
     * @var string
130
     */
131
    protected $moduleName = 'web_layout';
132
133
    /**
134
     * @var ModuleTemplate
135
     */
136
    protected $moduleTemplate;
137
138
    /**
139
     * @var ButtonBar
140
     */
141
    protected $buttonBar;
142
143
    /**
144
     * @var string
145
     */
146
    protected $searchContent;
147
148
    /**
149
     * @var SiteLanguage[]
150
     */
151
    protected $availableLanguages;
152
153
    /**
154
     * @var PageRenderer
155
     */
156
    protected $pageRenderer;
157
158
    /**
159
     * @var UriBuilder
160
     */
161
    protected $uriBuilder;
162
163
    /**
164
     * @var PageLayoutContext|null
165
     */
166
    protected $context;
167
168
    /**
169
     * Injects the request object for the current request or subrequest
170
     * As this controller goes only through the main() method, it is rather simple for now
171
     *
172
     * @param ServerRequestInterface $request the current request
173
     * @return ResponseInterface the response with the content
174
     */
175
    public function mainAction(ServerRequestInterface $request): ResponseInterface
176
    {
177
        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
178
        $this->pageRenderer = $this->moduleTemplate->getPageRenderer();
179
        $this->iconFactory = $this->moduleTemplate->getIconFactory();
180
        $this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
181
        $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
182
        $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
183
        // Setting module configuration / page select clause
184
        $this->id = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
185
186
        // Load page info array
187
        $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW));
188
        if ($this->pageinfo !== false) {
0 ignored issues
show
introduced by
The condition $this->pageinfo !== false is always true.
Loading history...
189
            // If page info is not resolved, user has no access or the ID parameter was malformed.
190
            $this->context = GeneralUtility::makeInstance(
191
                PageLayoutContext::class,
192
                $this->pageinfo,
193
                GeneralUtility::makeInstance(BackendLayoutView::class)->getBackendLayoutForPage($this->id)
194
            );
195
        }
196
197
        /** @var SiteInterface $currentSite */
198
        $currentSite = $request->getAttribute('site');
199
        $this->availableLanguages = $currentSite->getAvailableLanguages($this->getBackendUser(), false, $this->id);
200
        // initialize page/be_user TSconfig settings
201
        $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
202
        $this->modSharedTSconfig['properties'] = $pageTsConfig['mod.']['SHARED.'] ?? [];
203
        $this->modTSconfig['properties'] = $pageTsConfig['mod.']['web_layout.'] ?? [];
204
205
        // Initialize menu
206
        $this->menuConfig($request);
207
        // Setting sys language from session var
208
        $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
209
210
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Recordlist/ClearCache');
211
212
        $this->main($request);
213
        return new HtmlResponse($this->moduleTemplate->renderContent());
214
    }
215
216
    /**
217
     * Initialize menu array
218
     * @param ServerRequestInterface $request
219
     */
220
    protected function menuConfig(ServerRequestInterface $request): void
221
    {
222
        // MENU-ITEMS:
223
        $this->MOD_MENU = [
224
            'tt_content_showHidden' => '',
225
            'function' => [
226
                1 => $this->getLanguageService()->getLL('m_function_1'),
227
                2 => $this->getLanguageService()->getLL('m_function_2')
228
            ],
229
            'language' => [
230
                0 => $this->getLanguageService()->getLL('m_default')
231
            ]
232
        ];
233
234
        // First, select all localized page records on the current page.
235
        // Each represents a possibility for a language on the page. Add these to language selector.
236
        if ($this->id) {
237
            // Compile language data for pid != 0 only. The language drop-down is not shown on pid 0
238
            // since pid 0 can't be localized.
239
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
240
            $queryBuilder->getRestrictions()->removeAll()
241
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
242
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
243
            $statement = $queryBuilder->select('uid', $GLOBALS['TCA']['pages']['ctrl']['languageField'])
244
                ->from('pages')
245
                ->where(
246
                    $queryBuilder->expr()->eq(
247
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
248
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
249
                    )
250
                )->execute();
251
            while ($pageTranslation = $statement->fetch()) {
252
                $languageId = $pageTranslation[$GLOBALS['TCA']['pages']['ctrl']['languageField']];
253
                if (isset($this->availableLanguages[$languageId])) {
254
                    $this->MOD_MENU['language'][$languageId] = $this->availableLanguages[$languageId]->getTitle();
255
                }
256
            }
257
            // Override the label
258
            if (isset($this->availableLanguages[0])) {
259
                $this->MOD_MENU['language'][0] = $this->availableLanguages[0]->getTitle();
260
            }
261
        }
262
        // Initialize the available actions
263
        $actions = $this->initActions();
264
        // Clean up settings
265
        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $request->getParsedBody()['SET'] ?? $request->getQueryParams()['SET'] ?? [], $this->moduleName);
266
        // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
267
        if ($this->getBackendUser()->workspace != 0
268
            || !isset($this->MOD_SETTINGS['tt_content_showHidden'])
269
            || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0'
270
        ) {
271
            $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
272
        }
273
        // Make action menu from available actions
274
        $this->makeActionMenu($actions);
275
    }
276
277
    /**
278
     * Initializes the available actions this module provides
279
     *
280
     * @return array the available actions
281
     */
282
    protected function initActions(): array
283
    {
284
        $actions = [
285
            1 => $this->getLanguageService()->getLL('m_function_1')
286
        ];
287
        // Find if there are ANY languages at all (and if not, do not show the language option from function menu).
288
        if (count($this->availableLanguages) > 1) {
289
            $actions[2] = $this->getLanguageService()->getLL('m_function_2');
290
        }
291
        $this->makeLanguageMenu();
292
        // Page / user TSconfig blinding of menu-items
293
        $blindActions = $this->modTSconfig['properties']['menu.']['functions.'] ?? [];
294
        foreach ($blindActions as $key => $value) {
295
            if (!$value && array_key_exists($key, $actions)) {
296
                unset($actions[$key]);
297
            }
298
        }
299
300
        return $actions;
301
    }
302
303
    /**
304
     * This creates the dropdown menu with the different actions this module is able to provide.
305
     * For now they are Columns and Languages.
306
     *
307
     * @param array $actions array with the available actions
308
     */
309
    protected function makeActionMenu(array $actions): void
310
    {
311
        $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
312
        $actionMenu->setIdentifier('actionMenu');
313
        $actionMenu->setLabel('');
314
315
        $defaultKey = null;
316
        $foundDefaultKey = false;
317
        foreach ($actions as $key => $action) {
318
            $menuItem = $actionMenu
319
                ->makeMenuItem()
320
                ->setTitle($action)
321
                ->setHref((string)$this->uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
322
323
            if (!$foundDefaultKey) {
324
                $defaultKey = $key;
325
                $foundDefaultKey = true;
326
            }
327
            if ((int)$this->MOD_SETTINGS['function'] === $key) {
328
                $menuItem->setActive(true);
329
                $defaultKey = null;
330
            }
331
            $actionMenu->addMenuItem($menuItem);
332
        }
333
        if (isset($defaultKey)) {
334
            $this->MOD_SETTINGS['function'] = $defaultKey;
335
        }
336
        $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
337
    }
338
339
    /**
340
     * Generate the flashmessages for current pid
341
     *
342
     * @return string HTML content with flashmessages
343
     */
344
    protected function getHeaderFlashMessagesForCurrentPid(): string
345
    {
346
        $content = '';
347
        $lang = $this->getLanguageService();
348
349
        $view = GeneralUtility::makeInstance(StandaloneView::class);
350
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
351
352
        // If page is a folder
353
        if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
354
            $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
355
            $moduleLoader->load($GLOBALS['TBE_MODULES']);
356
            $modules = $moduleLoader->modules;
357
            if (is_array($modules['web']['sub']['list'])) {
358
                $title = $lang->getLL('goToListModule');
359
                $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
360
                $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\');">' . $lang->getLL('goToListModule') . '</a>';
361
                $view->assignMultiple([
362
                    'title' => $title,
363
                    'message' => $message,
364
                    'state' => InfoboxViewHelper::STATE_INFO
365
                ]);
366
                $content .= $view->render();
367
            }
368
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
369
            $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
370
            $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
371
            $targetPage = [];
372
            $message = '';
373
            $state = InfoboxViewHelper::STATE_ERROR;
374
375
            if ($shortcutMode || $this->pageinfo['shortcut']) {
376
                switch ($shortcutMode) {
377
                    case PageRepository::SHORTCUT_MODE_NONE:
378
                        $targetPage = $this->getTargetPageIfVisible($pageRepository->getPage($this->pageinfo['shortcut']));
379
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredOrNotAccessibleInternalLinkMessage') : '';
380
                        break;
381
                    case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
382
                        $menuOfPages = $pageRepository->getMenu($this->pageinfo['uid'], '*', 'sorting', 'AND hidden = 0');
383
                        $targetPage = reset($menuOfPages) ?: [];
384
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredFirstSubpageMessage') : '';
385
                        break;
386
                    case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
387
                        $targetPage = $this->getTargetPageIfVisible($pageRepository->getPage($this->pageinfo['pid']));
388
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredParentPageMessage') : '';
389
                        break;
390
                    case PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE:
391
                        $possibleTargetPages = $pageRepository->getMenu($this->pageinfo['uid'], '*', 'sorting', 'AND hidden = 0');
392
                        if ($possibleTargetPages === []) {
393
                            $message .= $lang->getLL('pageIsMisconfiguredOrNotAccessibleRandomInternalLinkMessage');
394
                            break;
395
                        }
396
                        $message = $lang->getLL('pageIsRandomInternalLinkMessage');
397
                        $state = InfoboxViewHelper::STATE_INFO;
398
                        break;
399
                }
400
                $message = htmlspecialchars($message);
401
                if ($targetPage !== [] && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
402
                    $linkToPid = GeneralUtility::linkThisScript(['id' => $targetPage['uid']]);
403
                    $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
404
                    $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($path) . '</a>';
0 ignored issues
show
Bug introduced by
It seems like $path can also be of type array<integer,string>; however, parameter $string of htmlspecialchars() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

404
                    $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars(/** @scrutinizer ignore-type */ $path) . '</a>';
Loading history...
405
                    $message .= sprintf(htmlspecialchars($lang->getLL('pageIsInternalLinkMessage')), $linkedPath);
406
                    $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', (string)$shortcutMode))) . ')';
407
                    $state = InfoboxViewHelper::STATE_INFO;
408
                }
409
            } else {
410
                $message = htmlspecialchars($lang->getLL('pageIsMisconfiguredInternalLinkMessage'));
411
                $state = InfoboxViewHelper::STATE_ERROR;
412
            }
413
414
            $view->assignMultiple([
415
                'title' => $this->pageinfo['title'],
416
                'message' => $message,
417
                'state' => $state
418
            ]);
419
            $content .= $view->render();
420
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
421
            if (empty($this->pageinfo['url'])) {
422
                $view->assignMultiple([
423
                    'title' => $this->pageinfo['title'],
424
                    'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
425
                    'state' => InfoboxViewHelper::STATE_ERROR
426
                ]);
427
                $content .= $view->render();
428
            } else {
429
                $externalUrl = GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo);
430
                if (is_string($externalUrl)) {
431
                    $externalUrl = htmlspecialchars($externalUrl);
432
                    $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noreferrer">' . $externalUrl . '</a>';
433
                    $view->assignMultiple([
434
                        'title' => $this->pageinfo['title'],
435
                        'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
436
                        'state' => InfoboxViewHelper::STATE_INFO
437
                    ]);
438
                    $content .= $view->render();
439
                }
440
            }
441
        }
442
        // If content from different pid is displayed
443
        if ($this->pageinfo['content_from_pid']) {
444
            $contentPage = (array)BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
445
            $linkToPid = GeneralUtility::linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
446
            $title = BackendUtility::getRecordTitle('pages', $contentPage);
447
            $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
448
            $message = sprintf($lang->getLL('content_from_pid_title'), $link);
449
            $view->assignMultiple([
450
                'title' => $title,
451
                'message' => $message,
452
                'state' => InfoboxViewHelper::STATE_INFO
453
            ]);
454
            $content .= $view->render();
455
        } else {
456
            $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
457
            if (!empty($links)) {
458
                $message = sprintf($lang->getLL('content_on_pid_title'), $links);
459
                $view->assignMultiple([
460
                    'title' => '',
461
                    'message' => $message,
462
                    'state' => InfoboxViewHelper::STATE_INFO
463
                ]);
464
                $content .= $view->render();
465
            }
466
        }
467
        return $content;
468
    }
469
470
    /**
471
     * Get all pages with links where the content of a page $pageId is also shown on
472
     *
473
     * @param int $pageId
474
     * @return string
475
     */
476
    protected function getPageLinksWhereContentIsAlsoShownOn($pageId): string
477
    {
478
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
479
        $queryBuilder->getRestrictions()->removeAll();
480
        $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
481
        $queryBuilder
482
            ->select('*')
483
            ->from('pages')
484
            ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
485
486
        $links = [];
487
        $rows = $queryBuilder->execute()->fetchAll();
488
        if (!empty($rows)) {
489
            foreach ($rows as $row) {
490
                $linkToPid = GeneralUtility::linkThisScript(['id' => $row['uid']]);
491
                $title = BackendUtility::getRecordTitle('pages', $row);
492
                $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
493
                $links[] = $link;
494
            }
495
        }
496
        return implode(', ', $links);
497
    }
498
499
    /**
500
     * @return string $title
501
     */
502
    protected function getLocalizedPageTitle(): string
503
    {
504
        if ($this->current_sys_language > 0) {
505
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
506
                ->getQueryBuilderForTable('pages');
507
            $queryBuilder->getRestrictions()
508
                ->removeAll()
509
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
510
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
511
            $localizedPage = $queryBuilder
512
                ->select('*')
513
                ->from('pages')
514
                ->where(
515
                    $queryBuilder->expr()->eq(
516
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
517
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
518
                    ),
519
                    $queryBuilder->expr()->eq(
520
                        $GLOBALS['TCA']['pages']['ctrl']['languageField'],
521
                        $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
522
                    )
523
                )
524
                ->setMaxResults(1)
525
                ->execute()
526
                ->fetch();
527
            BackendUtility::workspaceOL('pages', $localizedPage);
528
            return $localizedPage['title'];
529
        }
530
        return $this->pageinfo['title'];
531
    }
532
533
    /**
534
     * Main function.
535
     * Creates some general objects and calls other functions for the main rendering of module content.
536
     *
537
     * @param ServerRequestInterface $request
538
     */
539
    protected function main(ServerRequestInterface $request): void
540
    {
541
        $content = '';
542
        // Access check...
543
        // The page will show only if there is a valid page and if this page may be viewed by the user
544
        if ($this->id && is_array($this->pageinfo)) {
545
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
546
547
            $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
548
                if (top.fsMod) {
549
                    top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
550
                    top.fsMod.navFrameHighlightedID["web"] = top.fsMod.currentBank + "_" + ' . (int)$this->id . ';
551
                }
552
                function deleteRecord(table,id,url) {   //
553
                    window.location.href = ' . GeneralUtility::quoteJSvalue((string)$this->uriBuilder->buildUriFromRoute('tce_db') . '&cmd[')
554
                                            . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url);
555
                    return false;
556
                }
557
            ');
558
559
            if ($this->context instanceof PageLayoutContext) {
560
                $backendLayout = $this->context->getBackendLayout();
561
562
                // Find backend layout / columns
563
                if (!empty($backendLayout->getColumnPositionNumbers())) {
564
                    $this->colPosList = implode(',', $backendLayout->getColumnPositionNumbers());
565
                }
566
                // Removing duplicates, if any
567
                $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
0 ignored issues
show
Documentation Bug introduced by
It seems like array_unique(TYPO3\CMS\C...,', $this->colPosList)) of type array is incompatible with the declared type string of property $colPosList.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
568
                // Accessible columns
569
                if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
570
                    $this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
0 ignored issues
show
Documentation Bug introduced by
It seems like array_unique(TYPO3\CMS\C...ies']['colPos_list']))) of type array is incompatible with the declared type string of property $activeColPosList.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
571
                    // Match with the list which is present in the colPosList for the current page
572
                    if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
573
                        $this->activeColPosList = array_unique(array_intersect(
574
                            $this->activeColPosList,
575
                            $this->colPosList
576
                        ));
577
                    }
578
                } else {
579
                    $this->activeColPosList = $this->colPosList;
580
                }
581
                $this->activeColPosList = implode(',', $this->activeColPosList);
582
                $this->colPosList = implode(',', $this->colPosList);
583
            }
584
585
            $content .= $this->getHeaderFlashMessagesForCurrentPid();
586
587
            // Render the primary module content:
588
            $content .= '<form action="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->id])) . '" id="PageLayoutController" method="post">';
589
            // Page title
590
            $content .= '<h1 class="' . ($this->isPageEditable($this->current_sys_language) ? 't3js-title-inlineedit' : '') . '">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
591
            // All other listings
592
            $content .= $this->renderContent();
593
            $content .= '</form>';
594
            $content .= $this->searchContent;
595
            // Setting up the buttons for the docheader
596
            $this->makeButtons($request);
597
598
            // Create LanguageMenu
599
            $this->makeLanguageMenu();
600
        } else {
601
            $this->moduleTemplate->addJavaScriptCode(
602
                'mainJsFunctions',
603
                'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
604
            );
605
            $content .= '<h1>' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '</h1>';
606
            $view = GeneralUtility::makeInstance(StandaloneView::class);
607
            $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
608
            $view->assignMultiple([
609
                'title' => $this->getLanguageService()->getLL('clickAPage_header'),
610
                'message' => $this->getLanguageService()->getLL('clickAPage_content'),
611
                'state' => InfoboxViewHelper::STATE_INFO
612
            ]);
613
            $content .= $view->render();
614
        }
615
        // Set content
616
        $this->moduleTemplate->setContent($content);
617
    }
618
619
    /**
620
     * Rendering content
621
     *
622
     * @return string
623
     */
624
    protected function renderContent(): string
625
    {
626
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
627
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
628
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Localization');
629
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/DragDrop');
630
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
631
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/Paste');
632
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
633
634
        $tableOutput = '';
635
        $numberOfHiddenElements = 0;
636
637
        if ($this->context instanceof PageLayoutContext) {
638
            // Context may not be set, which happens if the page module is viewed by a user with no access to the
639
            // current page, or if the ID parameter is malformed. In this case we do not resolve any backend layout
640
            // or other page structure information and we do not render any "table output" for the module.
641
            $backendLayout = $this->context->getBackendLayout();
642
643
            $configuration = $this->context->getDrawingConfiguration();
644
            $configuration->setDefaultLanguageBinding(!empty($this->modTSconfig['properties']['defLangBinding']));
645
            $configuration->setActiveColumns(GeneralUtility::trimExplode(',', $this->activeColPosList));
646
            $configuration->setShowHidden((bool)$this->MOD_SETTINGS['tt_content_showHidden']);
647
            $configuration->setLanguageColumns($this->MOD_MENU['language']);
648
            $configuration->setShowNewContentWizard(empty($this->modTSconfig['properties']['disableNewContentElementWizard']));
649
            $configuration->setSelectedLanguageId((int)$this->MOD_SETTINGS['language']);
650
            if ($this->MOD_SETTINGS['function'] == 2) {
651
                $configuration->setLanguageMode(true);
652
            }
653
654
            $numberOfHiddenElements = $this->getNumberOfHiddenElements($configuration->getLanguageColumns());
655
656
            if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('fluidBasedPageModule')) {
657
                $pageLayoutDrawer = $this->context->getBackendLayoutRenderer();
658
659
                $pageActionsCallback = null;
660
                if ($this->context->isPageEditable()) {
661
                    $languageOverlayId = 0;
662
                    $pageLocalizationRecord = BackendUtility::getRecordLocalization('pages', $this->id, (int)$this->current_sys_language);
663
                    if (is_array($pageLocalizationRecord)) {
664
                        $pageLocalizationRecord = reset($pageLocalizationRecord);
665
                    }
666
                    if (!empty($pageLocalizationRecord['uid'])) {
667
                        $languageOverlayId = $pageLocalizationRecord['uid'];
668
                    }
669
                    $pageActionsCallback = 'function(PageActions) {
670
                        PageActions.setPageId(' . (int)$this->id . ');
671
                        PageActions.setLanguageOverlayId(' . $languageOverlayId . ');
672
                    }';
673
                }
674
                $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', $pageActionsCallback);
675
                $tableOutput = $pageLayoutDrawer->drawContent();
676
            } else {
677
                $dbList = PageLayoutView::createFromPageLayoutContext($this->context);
678
                // Setting up the tt_content columns to show
679
                $colList = array_keys($backendLayout->getUsedColumns());
680
                if ($this->colPosList !== '') {
681
                    $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
682
                }
683
                // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
684
                $dbList->tt_contentConfig['cols'] = implode(',', $colList);
685
                $tableOutput = $dbList->getTable_tt_content($this->id);
686
            }
687
        }
688
689
        if ($this->getBackendUser()->check('tables_select', 'tt_content') && $numberOfHiddenElements > 0) {
690
            // Toggle hidden ContentElements
691
            $tableOutput .= '
692
                <div class="checkbox">
693
                    <label for="checkTt_content_showHidden">
694
                        <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
695
                        ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
696
                    </label>
697
                </div>';
698
        }
699
700
        // Init the content
701
        $content = '';
702
        // Additional header content
703
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'] ?? [] as $hook) {
704
            $params = [];
705
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
706
        }
707
        $content .= $tableOutput;
708
        // Making search form:
709
        if (!$this->modTSconfig['properties']['disableSearchBox']) {
710
            $this->searchContent = $this->getSearchBox();
0 ignored issues
show
Bug introduced by
The method getSearchBox() 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

710
            /** @scrutinizer ignore-call */ 
711
            $this->searchContent = $this->getSearchBox();

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...
711
            if ($this->searchContent) {
712
                $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
713
                $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
714
                    ->setClasses('t3js-toggle-search-toolbox')
715
                    ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
716
                    ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
717
                    ->setHref('#');
718
                $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
719
            }
720
        }
721
        // Additional footer content
722
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'] ?? [] as $hook) {
723
            $params = [];
724
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
725
        }
726
        return $content;
727
    }
728
729
    /***************************
730
     *
731
     * Sub-content functions, rendering specific parts of the module content.
732
     *
733
     ***************************/
734
    /**
735
     * This creates the buttons for the modules
736
     * @param ServerRequestInterface $request
737
     */
738
    protected function makeButtons(ServerRequestInterface $request): void
739
    {
740
        // Add CSH (Context Sensitive Help) icon to tool bar
741
        $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
742
            ->setModuleName('_MOD_' . $this->moduleName)
743
            ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
744
        $this->buttonBar->addButton($contextSensitiveHelpButton);
745
        $lang = $this->getLanguageService();
746
        // View page
747
        $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
748
        // Exclude sysfolders, spacers and recycler by default
749
        $excludeDokTypes = [
750
            PageRepository::DOKTYPE_RECYCLER,
751
            PageRepository::DOKTYPE_SYSFOLDER,
752
            PageRepository::DOKTYPE_SPACER
753
        ];
754
        // Custom override of values
755
        if (isset($pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
756
            $excludeDokTypes = GeneralUtility::intExplode(
757
                ',',
758
                $pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
759
                true
760
            );
761
        }
762
763
        if (
764
            !in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
765
            && !VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
766
        ) {
767
            $languageParameter = $this->current_sys_language ? ('&L=' . $this->current_sys_language) : '';
768
            $onClick = BackendUtility::viewOnClick(
769
                $this->pageinfo['uid'],
770
                '',
771
                BackendUtility::BEgetRootLine($this->pageinfo['uid']),
772
                '',
773
                '',
774
                $languageParameter
775
            );
776
            $viewButton = $this->buttonBar->makeLinkButton()
777
                ->setOnClick($onClick)
778
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
779
                ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
780
                ->setHref('#');
781
782
            $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
783
        }
784
        // Shortcut
785
        $shortcutButton = $this->buttonBar->makeShortcutButton()
786
            ->setModuleName($this->moduleName)
787
            ->setArguments([
788
                'route' => $request->getQueryParams()['route'],
789
                'id' => (int)$this->id,
790
                'SET' => [
791
                    'tt_content_showHidden' => (bool)$this->MOD_SETTINGS['tt_content_showHidden'],
792
                    'function' => (int)$this->MOD_SETTINGS['function'],
793
                    'language' => (int)$this->current_sys_language,
794
                ]
795
            ]);
796
        $this->buttonBar->addButton($shortcutButton);
797
798
        // Cache
799
        if (empty($this->modTSconfig['properties']['disableAdvanced'])) {
800
            $clearCacheButton = $this->buttonBar->makeLinkButton()
801
                ->setHref('#')
802
                ->setDataAttributes(['id' => $this->pageinfo['uid']])
803
                ->setClasses('t3js-clear-page-cache')
804
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
805
                ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
806
            $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
807
        }
808
        if (empty($this->modTSconfig['properties']['disableIconToolbar'])) {
809
            // Edit page properties and page language overlay icons
810
            if ($this->isPageEditable(0)) {
811
                /** @var \TYPO3\CMS\Core\Http\NormalizedParams */
812
                $normalizedParams = $request->getAttribute('normalizedParams');
813
                // Edit localized pages only when one specific language is selected
814
                if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
815
                    $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
816
                    $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
817
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
818
                        ->getQueryBuilderForTable('pages');
819
                    $queryBuilder->getRestrictions()
820
                        ->removeAll()
821
                        ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
822
                        ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
823
                    $overlayRecord = $queryBuilder
824
                        ->select('uid')
825
                        ->from('pages')
826
                        ->where(
827
                            $queryBuilder->expr()->eq(
828
                                $localizationParentField,
829
                                $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
830
                            ),
831
                            $queryBuilder->expr()->eq(
832
                                $languageField,
833
                                $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
834
                            )
835
                        )
836
                        ->setMaxResults(1)
837
                        ->execute()
838
                        ->fetch();
839
                    BackendUtility::workspaceOL('pages', $overlayRecord, (int)$this->getBackendUser()->workspace);
840
                    // Edit button
841
                    $urlParameters = [
842
                        'edit' => [
843
                            'pages' => [
844
                                $overlayRecord['uid'] => 'edit'
845
                            ]
846
                        ],
847
                        'returnUrl' => $normalizedParams->getRequestUri(),
848
                    ];
849
850
                    $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
851
                    $editLanguageButton = $this->buttonBar->makeLinkButton()
852
                        ->setHref($url)
853
                        ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
854
                        ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
855
                    $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
856
                }
857
                $urlParameters = [
858
                    'edit' => [
859
                        'pages' => [
860
                            $this->id => 'edit'
861
                        ]
862
                    ],
863
                    'returnUrl' => $normalizedParams->getRequestUri(),
864
                ];
865
                $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
866
                $editPageButton = $this->buttonBar->makeLinkButton()
867
                    ->setHref($url)
868
                    ->setTitle($lang->getLL('editPageProperties'))
869
                    ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
870
                $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
871
            }
872
        }
873
    }
874
875
    /*******************************
876
     *
877
     * Other functions
878
     *
879
     ******************************/
880
    /**
881
     * Returns the number of hidden elements (including those hidden by start/end times)
882
     * on the current page (for the current sys_language)
883
     *
884
     * @param array $languageColumns
885
     * @return int
886
     */
887
    protected function getNumberOfHiddenElements(array $languageColumns): int
888
    {
889
        $andWhere = [];
890
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
891
        $queryBuilder->getRestrictions()
892
            ->removeAll()
893
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
894
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
895
896
        $queryBuilder
897
            ->count('uid')
898
            ->from('tt_content')
899
            ->where(
900
                $queryBuilder->expr()->eq(
901
                    'pid',
902
                    $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
903
                )
904
            );
905
906
        if (!empty($languageColumns)) {
907
            // Multi-language view is active
908
            if ($this->current_sys_language > 0) {
909
                $queryBuilder->andWhere(
910
                    $queryBuilder->expr()->in(
911
                        'sys_language_uid',
912
                        [0, $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)]
913
                    )
914
                );
915
            }
916
        } else {
917
            $queryBuilder->andWhere(
918
                $queryBuilder->expr()->eq(
919
                    'sys_language_uid',
920
                    $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
921
                )
922
            );
923
        }
924
925
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
926
            $andWhere[] = $queryBuilder->expr()->neq(
927
                'hidden',
928
                $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
929
            );
930
        }
931
932
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
933
            $andWhere[] = $queryBuilder->expr()->andX(
934
                $queryBuilder->expr()->neq(
935
                    'starttime',
936
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
937
                ),
938
                $queryBuilder->expr()->gt(
939
                    'starttime',
940
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
941
                )
942
            );
943
        }
944
945
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
946
            $andWhere[] = $queryBuilder->expr()->andX(
947
                $queryBuilder->expr()->neq(
948
                    'endtime',
949
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
950
                ),
951
                $queryBuilder->expr()->lte(
952
                    'endtime',
953
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
954
                )
955
            );
956
        }
957
958
        if (!empty($andWhere)) {
959
            $queryBuilder->andWhere(
960
                $queryBuilder->expr()->orX(...$andWhere)
961
            );
962
        }
963
964
        $count = $queryBuilder
965
            ->execute()
966
            ->fetchColumn(0);
967
968
        return (int)$count;
969
    }
970
971
    /**
972
     * Check if page can be edited by current user
973
     *
974
     * @param int $languageId
975
     * @return bool
976
     */
977
    protected function isPageEditable(int $languageId): bool
978
    {
979
        if ($this->getBackendUser()->isAdmin()) {
980
            return true;
981
        }
982
983
        return !$this->pageinfo['editlock']
984
            && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::PAGE_EDIT)
0 ignored issues
show
Bug introduced by
It seems like $this->pageinfo can also be of type boolean; however, parameter $row of TYPO3\CMS\Core\Authentic...n::doesUserHaveAccess() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

984
            && $this->getBackendUser()->doesUserHaveAccess(/** @scrutinizer ignore-type */ $this->pageinfo, Permission::PAGE_EDIT)
Loading history...
985
            && $this->getBackendUser()->checkLanguageAccess($languageId);
986
    }
987
988
    /**
989
     * Check if content can be edited by current user
990
     *
991
     * @param int $languageId
992
     * @return bool
993
     */
994
    protected function isContentEditable(int $languageId): bool
995
    {
996
        if ($this->getBackendUser()->isAdmin()) {
997
            return true;
998
        }
999
1000
        return !$this->pageinfo['editlock']
1001
            && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)
0 ignored issues
show
Bug introduced by
It seems like $this->pageinfo can also be of type boolean; however, parameter $row of TYPO3\CMS\Core\Authentic...n::doesUserHaveAccess() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

1001
            && $this->getBackendUser()->doesUserHaveAccess(/** @scrutinizer ignore-type */ $this->pageinfo, Permission::CONTENT_EDIT)
Loading history...
1002
            && $this->getBackendUser()->checkLanguageAccess($languageId);
1003
    }
1004
1005
    /**
1006
     * Returns LanguageService
1007
     *
1008
     * @return LanguageService
1009
     */
1010
    protected function getLanguageService(): LanguageService
1011
    {
1012
        return $GLOBALS['LANG'];
1013
    }
1014
1015
    /**
1016
     * Returns the current BE user.
1017
     *
1018
     * @return BackendUserAuthentication
1019
     */
1020
    protected function getBackendUser(): BackendUserAuthentication
1021
    {
1022
        return $GLOBALS['BE_USER'];
1023
    }
1024
1025
    /**
1026
     * Make the LanguageMenu
1027
     */
1028
    protected function makeLanguageMenu(): void
1029
    {
1030
        if (count($this->MOD_MENU['language']) > 1) {
1031
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1032
            $languageMenu->setIdentifier('languageMenu');
1033
            foreach ($this->MOD_MENU['language'] as $key => $language) {
1034
                $menuItem = $languageMenu
1035
                    ->makeMenuItem()
1036
                    ->setTitle($language)
1037
                    ->setHref((string)$this->uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1038
                if ((int)$this->current_sys_language === $key) {
1039
                    $menuItem->setActive(true);
1040
                }
1041
                $languageMenu->addMenuItem($menuItem);
1042
            }
1043
            $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1044
        }
1045
    }
1046
1047
    /**
1048
     * Returns the target page if visible
1049
     *
1050
     * @param array $targetPage
1051
     *
1052
     * @return array
1053
     */
1054
    protected function getTargetPageIfVisible(array $targetPage): array
1055
    {
1056
        return !(bool)($targetPage['hidden'] ?? false) ? $targetPage : [];
1057
    }
1058
1059
    /**
1060
     * Creates the search box
1061
     *
1062
     * @return string HTML for the search box
1063
     */
1064
    protected function getSearchBox(): string
1065
    {
1066
        if (!$this->getBackendUser()->check('modules', 'web_list')) {
1067
            return '';
1068
        }
1069
        $lang = $this->getLanguageService();
1070
        $listModule = $this->uriBuilder->buildUriFromRoute('web_list', ['id' => $this->id]);
1071
        // Make level selector:
1072
        $opt = [];
1073
1074
        // "New" generation of search levels ... based on TS config
1075
        $config = BackendUtility::getPagesTSconfig($this->id);
1076
        $searchLevelsFromTSconfig = $config['mod.']['web_list.']['searchLevel.']['items.'];
1077
        $searchLevelItems = [];
1078
1079
        // get translated labels for search levels from pagets
1080
        foreach ($searchLevelsFromTSconfig as $keySearchLevel => $labelConfigured) {
1081
            $label = $lang->sL('LLL:' . $labelConfigured);
1082
            if ($label === '') {
1083
                $label = $labelConfigured;
1084
            }
1085
            $searchLevelItems[$keySearchLevel] = $label;
1086
        }
1087
1088
        foreach ($searchLevelItems as $kv => $label) {
1089
            $opt[] = '<option value="' . $kv . '"' . ($kv === 0 ? ' selected="selected"' : '') . '>'
1090
                . htmlspecialchars($label)
1091
                . '</option>';
1092
        }
1093
        $searchLevelLabel = $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.search_levels');
1094
        $searchStringLabel = $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.searchString');
1095
        $lMenu = '<select class="form-control" name="search_levels" title="' . htmlspecialchars($searchLevelLabel) . '" id="search_levels">' . implode('', $opt) . '</select>';
1096
        return '<div class="db_list-searchbox-form db_list-searchbox-toolbar module-docheader-bar module-docheader-bar-search t3js-module-docheader-bar t3js-module-docheader-bar-search" id="db_list-searchbox-toolbar" style="display: none;">
1097
			<form action="' . htmlspecialchars((string)$listModule) . '" method="post">
1098
                <div id="typo3-dblist-search">
1099
                    <div class="panel panel-default">
1100
                        <div class="panel-body">
1101
                            <div class="row">
1102
                                <div class="form-group col-xs-12">
1103
                                    <label for="search_field">' . htmlspecialchars($searchStringLabel) . ': </label>
1104
									<input class="form-control" type="search" placeholder="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.enterSearchString')) . '" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchString')) . '" name="search_field" id="search_field" value="" />
1105
                                </div>
1106
                                <div class="form-group col-xs-12 col-sm-6">
1107
									<label for="search_levels">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.search_levels')) . ': </label>
1108
									' . $lMenu . '
1109
                                </div>
1110
                                <div class="form-group col-xs-12 col-sm-6">
1111
									<label for="showLimit">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.limit')) . ': </label>
1112
									<input class="form-control" type="number" min="0" max="10000" placeholder="10" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.limit')) . '" name="showLimit" id="showLimit" value="" />
1113
                                </div>
1114
                                <div class="form-group col-xs-12">
1115
                                    <div class="form-control-wrap">
1116
                                        <button type="submit" class="btn btn-default" name="search" title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.search')) . '">
1117
                                            ' . $this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.search')) . '
1118
                                        </button>
1119
                                    </div>
1120
                                </div>
1121
                            </div>
1122
                        </div>
1123
                    </div>
1124
                </div>
1125
            </form>
1126
        </div>';
1127
    }
1128
}
1129