Passed
Push — master ( 6c3a97...24f438 )
by
unknown
14:20
created

PageLayoutController::getModuleTemplate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
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\PreviewUriBuilder;
24
use TYPO3\CMS\Backend\Routing\UriBuilder;
25
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
26
use TYPO3\CMS\Backend\Template\ModuleTemplate;
27
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
28
use TYPO3\CMS\Backend\Utility\BackendUtility;
29
use TYPO3\CMS\Backend\View\BackendLayoutView;
30
use TYPO3\CMS\Backend\View\PageLayoutContext;
31
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
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
     * The name of the module
123
     *
124
     * @var string
125
     */
126
    protected $moduleName = 'web_layout';
127
128
    /**
129
     * @var ModuleTemplate
130
     */
131
    protected $moduleTemplate;
132
133
    /**
134
     * @var ButtonBar
135
     */
136
    protected $buttonBar;
137
138
    /**
139
     * @var string
140
     */
141
    protected $searchContent;
142
143
    /**
144
     * @var SiteLanguage[]
145
     */
146
    protected $availableLanguages;
147
148
    /**
149
     * @var PageLayoutContext|null
150
     */
151
    protected $context;
152
153
    protected IconFactory $iconFactory;
154
    protected PageRenderer $pageRenderer;
155
    protected UriBuilder $uriBuilder;
156
    protected PageRepository $pageRepository;
157
    protected ModuleTemplateFactory $moduleTemplateFactory;
158
159
    public function __construct(
160
        IconFactory $iconFactory,
161
        PageRenderer $pageRenderer,
162
        UriBuilder $uriBuilder,
163
        PageRepository $pageRepository,
164
        ModuleTemplateFactory $moduleTemplateFactory
165
    ) {
166
        $this->iconFactory = $iconFactory;
167
        $this->pageRenderer = $pageRenderer;
168
        $this->uriBuilder = $uriBuilder;
169
        $this->pageRepository = $pageRepository;
170
        $this->moduleTemplateFactory = $moduleTemplateFactory;
171
    }
172
    /**
173
     * Injects the request object for the current request or subrequest
174
     * As this controller goes only through the main() method, it is rather simple for now
175
     *
176
     * @param ServerRequestInterface $request the current request
177
     * @return ResponseInterface the response with the content
178
     */
179
    public function mainAction(ServerRequestInterface $request): ResponseInterface
180
    {
181
        $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
182
        $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
183
        $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
184
        // Setting module configuration / page select clause
185
        $this->id = (int)($request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? 0);
186
187
        // Load page info array
188
        $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW));
189
        if ($this->pageinfo !== false) {
190
            // If page info is not resolved, user has no access or the ID parameter was malformed.
191
            $this->context = GeneralUtility::makeInstance(
192
                PageLayoutContext::class,
193
                $this->pageinfo,
194
                GeneralUtility::makeInstance(BackendLayoutView::class)->getBackendLayoutForPage($this->id)
195
            );
196
        }
197
198
        /** @var SiteInterface $currentSite */
199
        $currentSite = $request->getAttribute('site');
200
        $this->availableLanguages = $currentSite->getAvailableLanguages($this->getBackendUser(), false, $this->id);
201
        // initialize page/be_user TSconfig settings
202
        $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
203
        $this->modSharedTSconfig['properties'] = $pageTsConfig['mod.']['SHARED.'] ?? [];
204
        $this->modTSconfig['properties'] = $pageTsConfig['mod.']['web_layout.'] ?? [];
205
206
        // Initialize menu
207
        $this->menuConfig($request);
208
        // Setting sys language from session var
209
        $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
210
211
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Recordlist/ClearCache');
212
213
        $this->moduleTemplate->setTitle(
214
            $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
215
            $this->pageinfo['title'] ?? ''
216
        );
217
218
        $this->main($request);
219
        return new HtmlResponse($this->moduleTemplate->renderContent());
220
    }
221
222
    /**
223
     * Initialize menu array
224
     * @param ServerRequestInterface $request
225
     */
226
    protected function menuConfig(ServerRequestInterface $request): void
227
    {
228
        // MENU-ITEMS:
229
        $this->MOD_MENU = [
230
            'tt_content_showHidden' => '',
231
            'function' => [
232
                1 => $this->getLanguageService()->getLL('m_function_1'),
233
                2 => $this->getLanguageService()->getLL('m_function_2')
234
            ],
235
            'language' => [
236
                0 => $this->getLanguageService()->getLL('m_default')
237
            ]
238
        ];
239
240
        // First, select all localized page records on the current page.
241
        // Each represents a possibility for a language on the page. Add these to language selector.
242
        if ($this->id) {
243
            // Compile language data for pid != 0 only. The language drop-down is not shown on pid 0
244
            // since pid 0 can't be localized.
245
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
246
            $queryBuilder->getRestrictions()->removeAll()
247
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
248
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
249
            $statement = $queryBuilder->select('uid', $GLOBALS['TCA']['pages']['ctrl']['languageField'])
250
                ->from('pages')
251
                ->where(
252
                    $queryBuilder->expr()->eq(
253
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
254
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
255
                    )
256
                )->execute();
257
            while ($pageTranslation = $statement->fetch()) {
258
                $languageId = $pageTranslation[$GLOBALS['TCA']['pages']['ctrl']['languageField']];
259
                if (isset($this->availableLanguages[$languageId])) {
260
                    $this->MOD_MENU['language'][$languageId] = $this->availableLanguages[$languageId]->getTitle();
261
                }
262
            }
263
            // Override the label
264
            if (isset($this->availableLanguages[0])) {
265
                $this->MOD_MENU['language'][0] = $this->availableLanguages[0]->getTitle();
266
            }
267
        }
268
        // Initialize the available actions
269
        $actions = $this->initActions();
270
        // Clean up settings
271
        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, $request->getParsedBody()['SET'] ?? $request->getQueryParams()['SET'] ?? [], $this->moduleName);
272
        // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
273
        if ($this->getBackendUser()->workspace != 0
274
            || !isset($this->MOD_SETTINGS['tt_content_showHidden'])
275
            || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0'
276
        ) {
277
            $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
278
        }
279
        // Make action menu from available actions
280
        $this->makeActionMenu($actions);
281
    }
282
283
    /**
284
     * Initializes the available actions this module provides
285
     *
286
     * @return array the available actions
287
     */
288
    protected function initActions(): array
289
    {
290
        $actions = [
291
            1 => $this->getLanguageService()->getLL('m_function_1')
292
        ];
293
        // Find if there are ANY languages at all (and if not, do not show the language option from function menu).
294
        if (count($this->availableLanguages) > 1) {
295
            $actions[2] = $this->getLanguageService()->getLL('m_function_2');
296
        }
297
        $this->makeLanguageMenu();
298
        // Page / user TSconfig blinding of menu-items
299
        $blindActions = $this->modTSconfig['properties']['menu.']['functions.'] ?? [];
300
        foreach ($blindActions as $key => $value) {
301
            if (!$value && array_key_exists($key, $actions)) {
302
                unset($actions[$key]);
303
            }
304
        }
305
306
        return $actions;
307
    }
308
309
    /**
310
     * This creates the dropdown menu with the different actions this module is able to provide.
311
     * For now they are Columns and Languages.
312
     *
313
     * @param array $actions array with the available actions
314
     */
315
    protected function makeActionMenu(array $actions): void
316
    {
317
        $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
318
        $actionMenu->setIdentifier('actionMenu');
319
        $actionMenu->setLabel('');
320
321
        $defaultKey = null;
322
        $foundDefaultKey = false;
323
        foreach ($actions as $key => $action) {
324
            $menuItem = $actionMenu
325
                ->makeMenuItem()
326
                ->setTitle($action)
327
                ->setHref((string)$this->uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
328
329
            if (!$foundDefaultKey) {
330
                $defaultKey = $key;
331
                $foundDefaultKey = true;
332
            }
333
            if ((int)$this->MOD_SETTINGS['function'] === $key) {
334
                $menuItem->setActive(true);
335
                $defaultKey = null;
336
            }
337
            $actionMenu->addMenuItem($menuItem);
338
        }
339
        if (isset($defaultKey)) {
340
            $this->MOD_SETTINGS['function'] = $defaultKey;
341
        }
342
        $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
343
    }
344
345
    /**
346
     * Generate the flashmessages for current pid
347
     *
348
     * @return string HTML content with flashmessages
349
     */
350
    protected function getHeaderFlashMessagesForCurrentPid(): string
351
    {
352
        $content = '';
353
        $lang = $this->getLanguageService();
354
355
        $view = GeneralUtility::makeInstance(StandaloneView::class);
356
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
357
358
        // If page is a folder
359
        if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
360
            $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
361
            $moduleLoader->load($GLOBALS['TBE_MODULES']);
362
            $modules = $moduleLoader->modules;
363
            if (is_array($modules['web']['sub']['list'])) {
364
                $title = $lang->getLL('goToListModule');
365
                $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
366
                $message .= '<a class="btn btn-info" data-dispatch-action="TYPO3.ModuleMenu.showModule" data-dispatch-args-list="web_list">'
367
                    . $lang->getLL('goToListModule') . '</a>';
368
                $view->assignMultiple([
369
                    'title' => $title,
370
                    'message' => $message,
371
                    'state' => InfoboxViewHelper::STATE_INFO
372
                ]);
373
                $content .= $view->render();
374
            }
375
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
376
            $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
377
            $targetPage = [];
378
            $message = '';
379
            $state = InfoboxViewHelper::STATE_ERROR;
380
381
            if ($shortcutMode || $this->pageinfo['shortcut']) {
382
                switch ($shortcutMode) {
383
                    case PageRepository::SHORTCUT_MODE_NONE:
384
                        $targetPage = $this->getTargetPageIfVisible($this->pageRepository->getPage($this->pageinfo['shortcut']));
385
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredOrNotAccessibleInternalLinkMessage') : '';
386
                        break;
387
                    case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
388
                        $menuOfPages = $this->pageRepository->getMenu($this->pageinfo['uid'], '*', 'sorting', 'AND hidden = 0');
389
                        $targetPage = reset($menuOfPages) ?: [];
390
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredFirstSubpageMessage') : '';
391
                        break;
392
                    case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
393
                        $targetPage = $this->getTargetPageIfVisible($this->pageRepository->getPage($this->pageinfo['pid']));
394
                        $message .= $targetPage === [] ? $lang->getLL('pageIsMisconfiguredParentPageMessage') : '';
395
                        break;
396
                    case PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE:
397
                        $possibleTargetPages = $this->pageRepository->getMenu($this->pageinfo['uid'], '*', 'sorting', 'AND hidden = 0');
398
                        if ($possibleTargetPages === []) {
399
                            $message .= $lang->getLL('pageIsMisconfiguredOrNotAccessibleRandomInternalLinkMessage');
400
                            break;
401
                        }
402
                        $message = $lang->getLL('pageIsRandomInternalLinkMessage');
403
                        $state = InfoboxViewHelper::STATE_INFO;
404
                        break;
405
                }
406
                $message = htmlspecialchars($message);
407
                if ($targetPage !== [] && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
408
                    $linkToPid = GeneralUtility::linkThisScript(['id' => $targetPage['uid']]);
409
                    $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
410
                    $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

410
                    $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars(/** @scrutinizer ignore-type */ $path) . '</a>';
Loading history...
411
                    $message .= sprintf(htmlspecialchars($lang->getLL('pageIsInternalLinkMessage')), $linkedPath);
412
                    $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', (string)$shortcutMode))) . ')';
413
                    $state = InfoboxViewHelper::STATE_INFO;
414
                }
415
            } else {
416
                $message = htmlspecialchars($lang->getLL('pageIsMisconfiguredInternalLinkMessage'));
417
                $state = InfoboxViewHelper::STATE_ERROR;
418
            }
419
420
            $view->assignMultiple([
421
                'title' => $this->pageinfo['title'],
422
                'message' => $message,
423
                'state' => $state
424
            ]);
425
            $content .= $view->render();
426
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
427
            if (empty($this->pageinfo['url'])) {
428
                $view->assignMultiple([
429
                    'title' => $this->pageinfo['title'],
430
                    'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
431
                    'state' => InfoboxViewHelper::STATE_ERROR
432
                ]);
433
                $content .= $view->render();
434
            } else {
435
                $externalUrl = $this->pageRepository->getExtURL($this->pageinfo);
0 ignored issues
show
Bug introduced by
It seems like $this->pageinfo can also be of type boolean; however, parameter $pagerow of TYPO3\CMS\Core\Domain\Re...Repository::getExtURL() 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

435
                $externalUrl = $this->pageRepository->getExtURL(/** @scrutinizer ignore-type */ $this->pageinfo);
Loading history...
436
                if (is_string($externalUrl)) {
437
                    $externalUrl = htmlspecialchars($externalUrl);
438
                    $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noreferrer">' . $externalUrl . '</a>';
439
                    $view->assignMultiple([
440
                        'title' => $this->pageinfo['title'],
441
                        'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
442
                        'state' => InfoboxViewHelper::STATE_INFO
443
                    ]);
444
                    $content .= $view->render();
445
                }
446
            }
447
        }
448
        // If content from different pid is displayed
449
        if ($this->pageinfo['content_from_pid']) {
450
            $contentPage = (array)BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
451
            $linkToPid = GeneralUtility::linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
452
            $title = BackendUtility::getRecordTitle('pages', $contentPage);
453
            $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
454
            $message = sprintf($lang->getLL('content_from_pid_title'), $link);
455
            $view->assignMultiple([
456
                'title' => $title,
457
                'message' => $message,
458
                'state' => InfoboxViewHelper::STATE_INFO
459
            ]);
460
            $content .= $view->render();
461
        } else {
462
            $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
463
            if (!empty($links)) {
464
                $message = sprintf($lang->getLL('content_on_pid_title'), $links);
465
                $view->assignMultiple([
466
                    'title' => '',
467
                    'message' => $message,
468
                    'state' => InfoboxViewHelper::STATE_INFO
469
                ]);
470
                $content .= $view->render();
471
            }
472
        }
473
        return $content;
474
    }
475
476
    /**
477
     * Get all pages with links where the content of a page $pageId is also shown on
478
     *
479
     * @param int $pageId
480
     * @return string
481
     */
482
    protected function getPageLinksWhereContentIsAlsoShownOn($pageId): string
483
    {
484
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
485
        $queryBuilder->getRestrictions()->removeAll();
486
        $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
487
        $queryBuilder
488
            ->select('*')
489
            ->from('pages')
490
            ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
491
492
        $links = [];
493
        $rows = $queryBuilder->execute()->fetchAll();
494
        if (!empty($rows)) {
495
            foreach ($rows as $row) {
496
                $linkToPid = GeneralUtility::linkThisScript(['id' => $row['uid']]);
497
                $title = BackendUtility::getRecordTitle('pages', $row);
498
                $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
499
                $links[] = $link;
500
            }
501
        }
502
        return implode(', ', $links);
503
    }
504
505
    /**
506
     * @return string $title
507
     */
508
    protected function getLocalizedPageTitle(): string
509
    {
510
        if ($this->current_sys_language > 0) {
511
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
512
                ->getQueryBuilderForTable('pages');
513
            $queryBuilder->getRestrictions()
514
                ->removeAll()
515
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
516
                ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
517
            $localizedPage = $queryBuilder
518
                ->select('*')
519
                ->from('pages')
520
                ->where(
521
                    $queryBuilder->expr()->eq(
522
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
523
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
524
                    ),
525
                    $queryBuilder->expr()->eq(
526
                        $GLOBALS['TCA']['pages']['ctrl']['languageField'],
527
                        $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
528
                    )
529
                )
530
                ->setMaxResults(1)
531
                ->execute()
532
                ->fetch();
533
            BackendUtility::workspaceOL('pages', $localizedPage);
534
            return $localizedPage['title'];
535
        }
536
        return $this->pageinfo['title'];
537
    }
538
539
    /**
540
     * Main function.
541
     * Creates some general objects and calls other functions for the main rendering of module content.
542
     *
543
     * @param ServerRequestInterface $request
544
     */
545
    protected function main(ServerRequestInterface $request): void
546
    {
547
        $content = '';
548
        // Access check...
549
        // The page will show only if there is a valid page and if this page may be viewed by the user
550
        if ($this->id && is_array($this->pageinfo)) {
551
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
552
553
            $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
554
                if (top.fsMod) {
555
                    top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
556
                    top.fsMod.navFrameHighlightedID["web"] = top.fsMod.currentBank + "_" + ' . (int)$this->id . ';
557
                }
558
                function deleteRecord(table,id,url) {   //
559
                    window.location.href = ' . GeneralUtility::quoteJSvalue((string)$this->uriBuilder->buildUriFromRoute('tce_db') . '&cmd[')
560
                                            . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url);
561
                    return false;
562
                }
563
            ');
564
565
            if ($this->context instanceof PageLayoutContext) {
566
                $backendLayout = $this->context->getBackendLayout();
567
568
                // Find backend layout / columns
569
                if (!empty($backendLayout->getColumnPositionNumbers())) {
570
                    $this->colPosList = implode(',', $backendLayout->getColumnPositionNumbers());
571
                }
572
                // Removing duplicates, if any
573
                $colPosArray = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
574
                // Accessible columns
575
                if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
576
                    $activeColPosArray = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
577
                    // Match with the list which is present in the colPosList for the current page
578
                    if (!empty($colPosArray) && !empty($activeColPosArray)) {
579
                        $activeColPosArray = array_unique(array_intersect(
580
                            $activeColPosArray,
581
                            $colPosArray
582
                        ));
583
                    }
584
                } else {
585
                    $activeColPosArray = $colPosArray;
586
                }
587
                $this->activeColPosList = implode(',', $activeColPosArray);
588
                $this->colPosList = implode(',', $colPosArray);
589
            }
590
591
            $content .= $this->getHeaderFlashMessagesForCurrentPid();
592
593
            // Render the primary module content:
594
            $content .= '<form action="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->id])) . '" id="PageLayoutController" method="post">';
595
            // Page title
596
            $content .= '<h1 class="' . ($this->isPageEditable($this->current_sys_language) ? 't3js-title-inlineedit' : '') . '">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
597
            // All other listings
598
            $content .= $this->renderContent();
599
            $content .= '</form>';
600
            // Setting up the buttons for the docheader
601
            $this->makeButtons($request);
602
603
            // Create LanguageMenu
604
            $this->makeLanguageMenu();
605
        } else {
606
            $this->moduleTemplate->addJavaScriptCode(
607
                'mainJsFunctions',
608
                'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
609
            );
610
            $content .= '<h1>' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '</h1>';
611
            $view = GeneralUtility::makeInstance(StandaloneView::class);
612
            $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
613
            $view->assignMultiple([
614
                'title' => $this->getLanguageService()->getLL('clickAPage_header'),
615
                'message' => $this->getLanguageService()->getLL('clickAPage_content'),
616
                'state' => InfoboxViewHelper::STATE_INFO
617
            ]);
618
            $content .= $view->render();
619
        }
620
        // Set content
621
        $this->moduleTemplate->setContent($content);
622
    }
623
624
    /**
625
     * Rendering content
626
     *
627
     * @return string
628
     */
629
    protected function renderContent(): string
630
    {
631
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
632
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
633
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Localization');
634
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/DragDrop');
635
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
636
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/Paste');
637
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
638
639
        $tableOutput = '';
640
        $numberOfHiddenElements = 0;
641
642
        if ($this->context instanceof PageLayoutContext) {
643
            // Context may not be set, which happens if the page module is viewed by a user with no access to the
644
            // current page, or if the ID parameter is malformed. In this case we do not resolve any backend layout
645
            // or other page structure information and we do not render any "table output" for the module.
646
            $backendLayout = $this->context->getBackendLayout();
0 ignored issues
show
Unused Code introduced by
The assignment to $backendLayout is dead and can be removed.
Loading history...
647
648
            $configuration = $this->context->getDrawingConfiguration();
649
            $configuration->setDefaultLanguageBinding(!empty($this->modTSconfig['properties']['defLangBinding']));
650
            $configuration->setActiveColumns(GeneralUtility::trimExplode(',', $this->activeColPosList));
651
            $configuration->setShowHidden((bool)$this->MOD_SETTINGS['tt_content_showHidden']);
652
            $configuration->setLanguageColumns($this->MOD_MENU['language']);
653
            $configuration->setShowNewContentWizard(empty($this->modTSconfig['properties']['disableNewContentElementWizard']));
654
            $configuration->setSelectedLanguageId((int)$this->MOD_SETTINGS['language']);
655
            if ($this->MOD_SETTINGS['function'] == 2) {
656
                $configuration->setLanguageMode(true);
657
            }
658
659
            $numberOfHiddenElements = $this->getNumberOfHiddenElements($configuration->getLanguageColumns());
660
661
            $pageLayoutDrawer = $this->context->getBackendLayoutRenderer();
662
663
            $pageActionsCallback = null;
664
            if ($this->context->isPageEditable()) {
665
                $languageOverlayId = 0;
666
                $pageLocalizationRecord = BackendUtility::getRecordLocalization('pages', $this->id, (int)$this->current_sys_language);
667
                if (is_array($pageLocalizationRecord)) {
668
                    $pageLocalizationRecord = reset($pageLocalizationRecord);
669
                }
670
                if (!empty($pageLocalizationRecord['uid'])) {
671
                    $languageOverlayId = $pageLocalizationRecord['uid'];
672
                }
673
                $pageActionsCallback = 'function(PageActions) {
674
                    PageActions.setPageId(' . (int)$this->id . ');
675
                    PageActions.setLanguageOverlayId(' . $languageOverlayId . ');
676
                }';
677
            }
678
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', $pageActionsCallback);
679
            $tableOutput = $pageLayoutDrawer->drawContent();
680
        }
681
682
        if ($this->getBackendUser()->check('tables_select', 'tt_content') && $numberOfHiddenElements > 0) {
683
            // Toggle hidden ContentElements
684
            $tableOutput .= '
685
                <div class="form-check">
686
                    <input type="checkbox" id="checkTt_content_showHidden" class="form-check-input" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
687
                    <label class="form-check-label" for="checkTt_content_showHidden">
688
                        ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
689
                    </label>
690
                </div>';
691
        }
692
693
        // Init the content
694
        $content = '';
695
        // Additional header content
696
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'] ?? [] as $hook) {
697
            $params = [];
698
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
699
        }
700
        $content .= $tableOutput;
701
702
        // Additional footer content
703
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'] ?? [] as $hook) {
704
            $params = [];
705
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
706
        }
707
        return $content;
708
    }
709
710
    /**
711
     * Make the ModuleTemplate public accessible for the use in hooks.
712
     *
713
     * @return ModuleTemplate
714
     */
715
    public function getModuleTemplate(): ModuleTemplate
716
    {
717
        return $this->moduleTemplate;
718
    }
719
720
    /***************************
721
     *
722
     * Sub-content functions, rendering specific parts of the module content.
723
     *
724
     ***************************/
725
    /**
726
     * This creates the buttons for the modules
727
     * @param ServerRequestInterface $request
728
     */
729
    protected function makeButtons(ServerRequestInterface $request): void
730
    {
731
        // Add CSH (Context Sensitive Help) icon to tool bar
732
        $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
733
            ->setModuleName('_MOD_' . $this->moduleName)
734
            ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
735
        $this->buttonBar->addButton($contextSensitiveHelpButton);
736
        $lang = $this->getLanguageService();
737
        // View page
738
        $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
739
        // Exclude sysfolders, spacers and recycler by default
740
        $excludeDokTypes = [
741
            PageRepository::DOKTYPE_RECYCLER,
742
            PageRepository::DOKTYPE_SYSFOLDER,
743
            PageRepository::DOKTYPE_SPACER
744
        ];
745
        // Custom override of values
746
        if (isset($pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
747
            $excludeDokTypes = GeneralUtility::intExplode(
748
                ',',
749
                $pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
750
                true
751
            );
752
        }
753
754
        if (
755
            !in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
756
            && !VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
757
        ) {
758
            $languageParameter = $this->current_sys_language ? ('&L=' . $this->current_sys_language) : '';
759
            $previewDataAttributes = PreviewUriBuilder::create((int)$this->pageinfo['uid'])
760
                ->withRootLine(BackendUtility::BEgetRootLine($this->pageinfo['uid']))
761
                ->withAdditionalQueryParameters($languageParameter)
762
                ->buildDispatcherDataAttributes();
763
            $viewButton = $this->buttonBar->makeLinkButton()
764
                ->setDataAttributes($previewDataAttributes ?? [])
765
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
766
                ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
767
                ->setHref('#');
768
769
            $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
770
        }
771
        // Shortcut
772
        $shortcutButton = $this->buttonBar->makeShortcutButton()
773
            ->setRouteIdentifier($this->moduleName)
774
            ->setDisplayName($this->getShortcutTitle())
775
            ->setArguments([
776
                'id' => (int)$this->id,
777
                'SET' => [
778
                    'tt_content_showHidden' => (bool)$this->MOD_SETTINGS['tt_content_showHidden'],
779
                    'function' => (int)$this->MOD_SETTINGS['function'],
780
                    'language' => (int)$this->current_sys_language,
781
                ]
782
            ]);
783
        $this->buttonBar->addButton($shortcutButton);
784
785
        // Cache
786
        $clearCacheButton = $this->buttonBar->makeLinkButton()
787
            ->setHref('#')
788
            ->setDataAttributes(['id' => $this->pageinfo['uid']])
789
            ->setClasses('t3js-clear-page-cache')
790
            ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
791
            ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
792
        $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
793
794
        // Edit page properties and page language overlay icons
795
        if ($this->isPageEditable(0)) {
796
            /** @var \TYPO3\CMS\Core\Http\NormalizedParams */
797
            $normalizedParams = $request->getAttribute('normalizedParams');
798
            // Edit localized pages only when one specific language is selected
799
            if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
800
                $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
801
                $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
802
                $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
803
                    ->getQueryBuilderForTable('pages');
804
                $queryBuilder->getRestrictions()
805
                    ->removeAll()
806
                    ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
807
                    ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
808
                $overlayRecord = $queryBuilder
809
                    ->select('uid')
810
                    ->from('pages')
811
                    ->where(
812
                        $queryBuilder->expr()->eq(
813
                            $localizationParentField,
814
                            $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
815
                        ),
816
                        $queryBuilder->expr()->eq(
817
                            $languageField,
818
                            $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
819
                        )
820
                    )
821
                    ->setMaxResults(1)
822
                    ->execute()
823
                    ->fetch();
824
                BackendUtility::workspaceOL('pages', $overlayRecord, (int)$this->getBackendUser()->workspace);
825
                // Edit button
826
                $urlParameters = [
827
                    'edit' => [
828
                        'pages' => [
829
                            $overlayRecord['uid'] => 'edit'
830
                        ]
831
                    ],
832
                    'returnUrl' => $normalizedParams->getRequestUri(),
833
                ];
834
835
                $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
836
                $editLanguageButton = $this->buttonBar->makeLinkButton()
837
                    ->setHref($url)
838
                    ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
839
                    ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
840
                $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
841
            }
842
            $urlParameters = [
843
                'edit' => [
844
                    'pages' => [
845
                        $this->id => 'edit'
846
                    ]
847
                ],
848
                'returnUrl' => $normalizedParams->getRequestUri(),
849
            ];
850
            $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
851
            $editPageButton = $this->buttonBar->makeLinkButton()
852
                ->setHref($url)
853
                ->setTitle($lang->getLL('editPageProperties'))
854
                ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
855
            $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
856
        }
857
    }
858
859
    /*******************************
860
     *
861
     * Other functions
862
     *
863
     ******************************/
864
    /**
865
     * Returns the number of hidden elements (including those hidden by start/end times)
866
     * on the current page (for the current sys_language)
867
     *
868
     * @param array $languageColumns
869
     * @return int
870
     */
871
    protected function getNumberOfHiddenElements(array $languageColumns): int
872
    {
873
        $andWhere = [];
874
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
875
        $queryBuilder->getRestrictions()
876
            ->removeAll()
877
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
878
            ->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->getBackendUser()->workspace));
879
880
        $queryBuilder
881
            ->count('uid')
882
            ->from('tt_content')
883
            ->where(
884
                $queryBuilder->expr()->eq(
885
                    'pid',
886
                    $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
887
                )
888
            );
889
890
        if (!empty($languageColumns)) {
891
            // Multi-language view is active
892
            if ($this->current_sys_language > 0) {
893
                $queryBuilder->andWhere(
894
                    $queryBuilder->expr()->in(
895
                        'sys_language_uid',
896
                        [0, $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)]
897
                    )
898
                );
899
            }
900
        } else {
901
            $queryBuilder->andWhere(
902
                $queryBuilder->expr()->eq(
903
                    'sys_language_uid',
904
                    $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
905
                )
906
            );
907
        }
908
909
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
910
            $andWhere[] = $queryBuilder->expr()->neq(
911
                'hidden',
912
                $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
913
            );
914
        }
915
916
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
917
            $andWhere[] = $queryBuilder->expr()->andX(
918
                $queryBuilder->expr()->neq(
919
                    'starttime',
920
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
921
                ),
922
                $queryBuilder->expr()->gt(
923
                    'starttime',
924
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
925
                )
926
            );
927
        }
928
929
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
930
            $andWhere[] = $queryBuilder->expr()->andX(
931
                $queryBuilder->expr()->neq(
932
                    'endtime',
933
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
934
                ),
935
                $queryBuilder->expr()->lte(
936
                    'endtime',
937
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
938
                )
939
            );
940
        }
941
942
        if (!empty($andWhere)) {
943
            $queryBuilder->andWhere(
944
                $queryBuilder->expr()->orX(...$andWhere)
945
            );
946
        }
947
948
        $count = $queryBuilder
949
            ->execute()
950
            ->fetchColumn(0);
951
952
        return (int)$count;
953
    }
954
955
    /**
956
     * Check if page can be edited by current user
957
     *
958
     * @param int $languageId
959
     * @return bool
960
     */
961
    protected function isPageEditable(int $languageId): bool
962
    {
963
        if ($this->getBackendUser()->isAdmin()) {
964
            return true;
965
        }
966
967
        return !$this->pageinfo['editlock']
968
            && $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

968
            && $this->getBackendUser()->doesUserHaveAccess(/** @scrutinizer ignore-type */ $this->pageinfo, Permission::PAGE_EDIT)
Loading history...
969
            && $this->getBackendUser()->checkLanguageAccess($languageId);
970
    }
971
972
    /**
973
     * Check if content can be edited by current user
974
     *
975
     * @param int $languageId
976
     * @return bool
977
     */
978
    protected function isContentEditable(int $languageId): bool
979
    {
980
        if ($this->getBackendUser()->isAdmin()) {
981
            return true;
982
        }
983
984
        return !$this->pageinfo['editlock']
985
            && $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

985
            && $this->getBackendUser()->doesUserHaveAccess(/** @scrutinizer ignore-type */ $this->pageinfo, Permission::CONTENT_EDIT)
Loading history...
986
            && $this->getBackendUser()->checkLanguageAccess($languageId);
987
    }
988
989
    /**
990
     * Returns LanguageService
991
     *
992
     * @return LanguageService
993
     */
994
    protected function getLanguageService(): LanguageService
995
    {
996
        return $GLOBALS['LANG'];
997
    }
998
999
    /**
1000
     * Returns the current BE user.
1001
     *
1002
     * @return BackendUserAuthentication
1003
     */
1004
    protected function getBackendUser(): BackendUserAuthentication
1005
    {
1006
        return $GLOBALS['BE_USER'];
1007
    }
1008
1009
    /**
1010
     * Make the LanguageMenu
1011
     */
1012
    protected function makeLanguageMenu(): void
1013
    {
1014
        if (count($this->MOD_MENU['language']) > 1) {
1015
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1016
            $languageMenu->setIdentifier('languageMenu');
1017
            foreach ($this->MOD_MENU['language'] as $key => $language) {
1018
                $menuItem = $languageMenu
1019
                    ->makeMenuItem()
1020
                    ->setTitle($language)
1021
                    ->setHref((string)$this->uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1022
                if ((int)$this->current_sys_language === $key) {
1023
                    $menuItem->setActive(true);
1024
                }
1025
                $languageMenu->addMenuItem($menuItem);
1026
            }
1027
            $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1028
        }
1029
    }
1030
1031
    /**
1032
     * Returns the target page if visible
1033
     *
1034
     * @param array $targetPage
1035
     *
1036
     * @return array
1037
     */
1038
    protected function getTargetPageIfVisible(array $targetPage): array
1039
    {
1040
        return !(bool)($targetPage['hidden'] ?? false) ? $targetPage : [];
1041
    }
1042
1043
    /**
1044
     * Returns the shortcut title for the current page
1045
     *
1046
     * @return string
1047
     */
1048
    protected function getShortcutTitle(): string
1049
    {
1050
        return sprintf(
1051
            '%s: %s [%d]',
1052
            $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tablabel'),
1053
            BackendUtility::getRecordTitle('pages', (array)$this->pageinfo),
1054
            $this->id
1055
        );
1056
    }
1057
}
1058