Passed
Push — master ( b6b62d...8900ad )
by
unknown
16:07
created

PageLayoutController::mainAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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

394
                    $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars(/** @scrutinizer ignore-type */ $path) . '</a>';
Loading history...
395
                    $message .= sprintf(htmlspecialchars($lang->getLL('pageIsInternalLinkMessage')), $linkedPath);
396
                    $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', $shortcutMode))) . ')';
397
                    $state = InfoboxViewHelper::STATE_INFO;
398
                }
399
            } else {
400
                $message = htmlspecialchars($lang->getLL('pageIsMisconfiguredInternalLinkMessage'));
401
                $state = InfoboxViewHelper::STATE_ERROR;
402
            }
403
404
            $view->assignMultiple([
405
                'title' => $this->pageinfo['title'],
406
                'message' => $message,
407
                'state' => $state
408
            ]);
409
            $content .= $view->render();
410
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
411
            if (empty($this->pageinfo['url'])) {
412
                $view->assignMultiple([
413
                    'title' => $this->pageinfo['title'],
414
                    'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
415
                    'state' => InfoboxViewHelper::STATE_ERROR
416
                ]);
417
                $content .= $view->render();
418
            } else {
419
                $externalUrl = htmlspecialchars(GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo));
420
                if ($externalUrl !== false) {
421
                    $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noreferrer">' . $externalUrl . '</a>';
422
                    $view->assignMultiple([
423
                        'title' => $this->pageinfo['title'],
424
                        'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
425
                        'state' => InfoboxViewHelper::STATE_INFO
426
                    ]);
427
                    $content .= $view->render();
428
                }
429
            }
430
        }
431
        // If content from different pid is displayed
432
        if ($this->pageinfo['content_from_pid']) {
433
            $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
434
            $linkToPid = GeneralUtility::linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
435
            $title = BackendUtility::getRecordTitle('pages', $contentPage);
436
            $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
437
            $message = sprintf($lang->getLL('content_from_pid_title'), $link);
438
            $view->assignMultiple([
439
                'title' => $title,
440
                'message' => $message,
441
                'state' => InfoboxViewHelper::STATE_INFO
442
            ]);
443
            $content .= $view->render();
444
        } else {
445
            $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
446
            if (!empty($links)) {
447
                $message = sprintf($lang->getLL('content_on_pid_title'), $links);
448
                $view->assignMultiple([
449
                    'title' => '',
450
                    'message' => $message,
451
                    'state' => InfoboxViewHelper::STATE_INFO
452
                ]);
453
                $content .= $view->render();
454
            }
455
        }
456
        return $content;
457
    }
458
459
    /**
460
     * Get all pages with links where the content of a page $pageId is also shown on
461
     *
462
     * @param int $pageId
463
     * @return string
464
     */
465
    protected function getPageLinksWhereContentIsAlsoShownOn($pageId): string
466
    {
467
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
468
        $queryBuilder->getRestrictions()->removeAll();
469
        $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
470
        $queryBuilder
471
            ->select('*')
472
            ->from('pages')
473
            ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
474
475
        $links = [];
476
        $rows = $queryBuilder->execute()->fetchAll();
477
        if (!empty($rows)) {
478
            foreach ($rows as $row) {
479
                $linkToPid = GeneralUtility::linkThisScript(['id' => $row['uid']]);
480
                $title = BackendUtility::getRecordTitle('pages', $row);
481
                $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
482
                $links[] = $link;
483
            }
484
        }
485
        return implode(', ', $links);
486
    }
487
488
    /**
489
     * @return string $title
490
     */
491
    protected function getLocalizedPageTitle(): string
492
    {
493
        if ($this->current_sys_language > 0) {
494
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
495
                ->getQueryBuilderForTable('pages');
496
            $queryBuilder->getRestrictions()
497
                ->removeAll()
498
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
499
                ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
500
            $localizedPage = $queryBuilder
501
                ->select('*')
502
                ->from('pages')
503
                ->where(
504
                    $queryBuilder->expr()->eq(
505
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
506
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
507
                    ),
508
                    $queryBuilder->expr()->eq(
509
                        $GLOBALS['TCA']['pages']['ctrl']['languageField'],
510
                        $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
511
                    )
512
                )
513
                ->setMaxResults(1)
514
                ->execute()
515
                ->fetch();
516
            BackendUtility::workspaceOL('pages', $localizedPage);
517
            return $localizedPage['title'];
518
        }
519
        return $this->pageinfo['title'];
520
    }
521
522
    /**
523
     * Main function.
524
     * Creates some general objects and calls other functions for the main rendering of module content.
525
     *
526
     * @param ServerRequestInterface $request
527
     */
528
    protected function main(ServerRequestInterface $request): void
529
    {
530
        $content = '';
531
        // Access check...
532
        // The page will show only if there is a valid page and if this page may be viewed by the user
533
        if ($this->id && is_array($this->pageinfo)) {
534
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
535
536
            $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
537
                if (top.fsMod) {
538
                    top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
539
                    top.fsMod.navFrameHighlightedID["web"] = top.fsMod.currentBank + "_" + ' . (int)$this->id . ';
540
                }
541
                function deleteRecord(table,id,url) {   //
542
                    window.location.href = ' . GeneralUtility::quoteJSvalue((string)$this->uriBuilder->buildUriFromRoute('tce_db') . '&cmd[')
543
                                            . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url);
544
                    return false;
545
                }
546
            ');
547
548
            // Find backend layout / columns
549
            $backendLayout = $this->backendLayouts->getSelectedBackendLayout($this->id);
550
            if (!empty($backendLayout['__colPosList'])) {
551
                $this->colPosList = implode(',', $backendLayout['__colPosList']);
552
            }
553
            // Removing duplicates, if any
554
            $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...
555
            // Accessible columns
556
            if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
557
                $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...
558
                // Match with the list which is present in the colPosList for the current page
559
                if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
560
                    $this->activeColPosList = array_unique(array_intersect(
561
                        $this->activeColPosList,
562
                        $this->colPosList
563
                    ));
564
                }
565
            } else {
566
                $this->activeColPosList = $this->colPosList;
567
            }
568
            $this->activeColPosList = implode(',', $this->activeColPosList);
569
            $this->colPosList = implode(',', $this->colPosList);
570
571
            $content .= $this->getHeaderFlashMessagesForCurrentPid();
572
573
            // Render the primary module content:
574
            $content .= '<form action="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->id])) . '" id="PageLayoutController" method="post">';
575
            // Page title
576
            $content .= '<h1 class="' . ($this->isPageEditable($this->current_sys_language) ? 't3js-title-inlineedit' : '') . '">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
577
            // All other listings
578
            $content .= $this->renderContent();
579
            $content .= '</form>';
580
            $content .= $this->searchContent;
581
            // Setting up the buttons for the docheader
582
            $this->makeButtons($request);
583
584
            // Create LanguageMenu
585
            $this->makeLanguageMenu();
586
        } else {
587
            $this->moduleTemplate->addJavaScriptCode(
588
                'mainJsFunctions',
589
                'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
590
            );
591
            $content .= '<h1>' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '</h1>';
592
            $view = GeneralUtility::makeInstance(StandaloneView::class);
593
            $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
594
            $view->assignMultiple([
595
                'title' => $this->getLanguageService()->getLL('clickAPage_header'),
596
                'message' => $this->getLanguageService()->getLL('clickAPage_content'),
597
                'state' => InfoboxViewHelper::STATE_INFO
598
            ]);
599
            $content .= $view->render();
600
        }
601
        // Set content
602
        $this->moduleTemplate->setContent($content);
603
    }
604
605
    /**
606
     * Rendering content
607
     *
608
     * @return string
609
     */
610
    protected function renderContent(): string
611
    {
612
        $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
613
614
        if (GeneralUtility::makeInstance(Features::class)->isFeatureEnabled('fluidBasedPageModule')) {
615
            $selectedCombinedIdentifier = $this->backendLayouts->getSelectedCombinedIdentifier($this->id);
616
            // If no backend layout is selected, use default
617
            if (empty($selectedCombinedIdentifier)) {
618
                $selectedCombinedIdentifier = 'default';
619
            }
620
621
            $backendLayout = $this->backendLayouts->getDataProviderCollection()->getBackendLayout(
622
                $selectedCombinedIdentifier,
623
                $this->id
624
            );
625
626
            // If backend layout is not found available anymore, use default
627
            if ($backendLayout === null) {
628
                $backendLayout = $this->backendLayouts->getDataProviderCollection()->getBackendLayout(
629
                    'default',
630
                    $this->id
631
                );
632
            }
633
634
            $configuration = $backendLayout->getDrawingConfiguration();
635
            $configuration->setPageId($this->id);
636
            $configuration->setDefaultLanguageBinding(!empty($this->modTSconfig['properties']['defLangBinding']));
637
            $configuration->setActiveColumns(GeneralUtility::trimExplode(',', $this->activeColPosList));
638
            $configuration->setShowHidden((bool)$this->MOD_SETTINGS['tt_content_showHidden']);
639
            $configuration->setLanguageColumns(array_combine(array_keys($this->MOD_MENU['language']), array_keys($this->MOD_MENU['language'])));
0 ignored issues
show
Bug introduced by
It seems like array_combine(array_keys...>MOD_MENU['language'])) can also be of type false; however, parameter $languageColumns of TYPO3\CMS\Backend\View\D...n::setLanguageColumns() 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

639
            $configuration->setLanguageColumns(/** @scrutinizer ignore-type */ array_combine(array_keys($this->MOD_MENU['language']), array_keys($this->MOD_MENU['language'])));
Loading history...
640
            $configuration->setLanguageColumnsPointer((int)$this->current_sys_language);
641
            if ($this->MOD_SETTINGS['function'] == 2) {
642
                $configuration->setLanguageMode($this->MOD_SETTINGS['function'] == 2);
643
            }
644
645
            $pageLayoutDrawer = $backendLayout->getBackendLayoutRenderer();
646
            $configuration->setShowNewContentWizard(empty($this->modTSconfig['properties']['disableNewContentElementWizard']));
647
648
            $pageActionsCallback = null;
649
            if ($configuration->isPageEditable()) {
650
                $languageOverlayId = 0;
651
                $pageLocalizationRecord = BackendUtility::getRecordLocalization('pages', $this->id, (int)$this->current_sys_language);
652
                if (is_array($pageLocalizationRecord)) {
653
                    $pageLocalizationRecord = reset($pageLocalizationRecord);
654
                }
655
                if (!empty($pageLocalizationRecord['uid'])) {
656
                    $languageOverlayId = $pageLocalizationRecord['uid'];
657
                }
658
                $pageActionsCallback = 'function(PageActions) {
659
                    PageActions.setPageId(' . (int)$this->id . ');
660
                    PageActions.setLanguageOverlayId(' . $languageOverlayId . ');
661
                }';
662
            }
663
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/PageActions', $pageActionsCallback);
664
            $numberOfHiddenElements = $this->getNumberOfHiddenElements($configuration->getLanguageColumns());
665
            $tableOutput = $pageLayoutDrawer->drawContent();
666
        } else {
667
            $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
668
            $dbList->doEdit = $this->isContentEditable($this->current_sys_language);
669
            $dbList->option_newWizard = empty($this->modTSconfig['properties']['disableNewContentElementWizard']);
670
            $dbList->defLangBinding = !empty($this->modTSconfig['properties']['defLangBinding']);
671
            $tcaItems = $this->backendLayouts->getColPosListItemsParsed($this->id);
672
            $numberOfHiddenElements = $this->getNumberOfHiddenElements(is_array($dbList->tt_contentConfig['languageCols']) ? $dbList->tt_contentConfig['languageCols'] : []);
673
            // Setting up the tt_content columns to show:
674
            if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
675
                $colList = [];
676
                foreach ($tcaItems as $temp) {
677
                    $colList[] = $temp[1];
678
                }
679
            } else {
680
                // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
681
                $colList = ['1', '0', '2', '3'];
682
            }
683
            if ($this->colPosList !== '') {
684
                $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
685
            }
686
            // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
687
            $dbList->tt_contentConfig['cols'] = implode(',', $colList);
688
            $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
689
            $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
690
            $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
691
            // If the function menu is set to "Language":
692
            if ($this->MOD_SETTINGS['function'] == 2) {
693
                $dbList->tt_contentConfig['languageMode'] = 1;
694
                $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
695
                $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
696
            }
697
            $tableOutput = $dbList->getTable_tt_content($this->id);
698
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tooltip');
699
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Localization');
700
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/DragDrop');
701
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
702
            $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LayoutModule/Paste');
703
        }
704
705
        $this->pageRenderer->addInlineLanguageLabelFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
706
        $h_func_b = '';
707
        if ($this->getBackendUser()->check('tables_select', 'tt_content')) {
708
            // Toggle hidden ContentElements
709
710
            if ($numberOfHiddenElements > 0) {
711
                $h_func_b = '
712
                    <div class="checkbox">
713
                        <label for="checkTt_content_showHidden">
714
                            <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
715
                            ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
716
                        </label>
717
                    </div>';
718
            }
719
        }
720
        $tableOutput .= $h_func_b;
721
722
        // Init the content
723
        $content = '';
724
        // Additional header content
725
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'] ?? [] as $hook) {
726
            $params = [];
727
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
728
        }
729
        $content .= $tableOutput;
730
        // Making search form:
731
        if (!$this->modTSconfig['properties']['disableSearchBox']) {
732
            $this->searchContent = $this->getSearchBox();
733
            if ($this->searchContent) {
734
                $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
735
                $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
736
                    ->setClasses('t3js-toggle-search-toolbox')
737
                    ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
738
                    ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
739
                    ->setHref('#');
740
                $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
741
            }
742
        }
743
        // Additional footer content
744
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'] ?? [] as $hook) {
745
            $params = [];
746
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
747
        }
748
        return $content;
749
    }
750
751
    /***************************
752
     *
753
     * Sub-content functions, rendering specific parts of the module content.
754
     *
755
     ***************************/
756
    /**
757
     * This creates the buttons for the modules
758
     * @param ServerRequestInterface $request
759
     */
760
    protected function makeButtons(ServerRequestInterface $request): void
761
    {
762
        // Add CSH (Context Sensitive Help) icon to tool bar
763
        $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
764
            ->setModuleName('_MOD_' . $this->moduleName)
765
            ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
766
        $this->buttonBar->addButton($contextSensitiveHelpButton);
767
        $lang = $this->getLanguageService();
768
        // View page
769
        $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
770
        // Exclude sysfolders, spacers and recycler by default
771
        $excludeDokTypes = [
772
            PageRepository::DOKTYPE_RECYCLER,
773
            PageRepository::DOKTYPE_SYSFOLDER,
774
            PageRepository::DOKTYPE_SPACER
775
        ];
776
        // Custom override of values
777
        if (isset($pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
778
            $excludeDokTypes = GeneralUtility::intExplode(
779
                ',',
780
                $pageTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
781
                true
782
            );
783
        }
784
785
        if (
786
            !in_array((int)$this->pageinfo['doktype'], $excludeDokTypes, true)
787
            && !VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)
788
        ) {
789
            $languageParameter = $this->current_sys_language ? ('&L=' . $this->current_sys_language) : '';
790
            $onClick = BackendUtility::viewOnClick(
791
                $this->pageinfo['uid'],
792
                '',
793
                BackendUtility::BEgetRootLine($this->pageinfo['uid']),
794
                '',
795
                '',
796
                $languageParameter
797
            );
798
            $viewButton = $this->buttonBar->makeLinkButton()
799
                ->setOnClick($onClick)
800
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
801
                ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
802
                ->setHref('#');
803
804
            $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
805
        }
806
        // Shortcut
807
        $shortcutButton = $this->buttonBar->makeShortcutButton()
808
            ->setModuleName($this->moduleName)
809
            ->setGetVariables([
810
                'id',
811
                'route',
812
                'edit_record',
813
            ])
814
            ->setSetVariables(array_keys($this->MOD_MENU));
815
        $this->buttonBar->addButton($shortcutButton);
816
817
        // Cache
818
        if (empty($this->modTSconfig['properties']['disableAdvanced'])) {
819
            $clearCacheButton = $this->buttonBar->makeLinkButton()
820
                ->setHref('#')
821
                ->setDataAttributes(['id' => $this->pageinfo['uid']])
822
                ->setClasses('t3js-clear-page-cache')
823
                ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
824
                ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
825
            $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
826
        }
827
        if (empty($this->modTSconfig['properties']['disableIconToolbar'])) {
828
            // Edit page properties and page language overlay icons
829
            if ($this->isPageEditable(0)) {
830
                /** @var \TYPO3\CMS\Core\Http\NormalizedParams */
831
                $normalizedParams = $request->getAttribute('normalizedParams');
832
                // Edit localized pages only when one specific language is selected
833
                if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
834
                    $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
835
                    $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
836
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
837
                        ->getQueryBuilderForTable('pages');
838
                    $queryBuilder->getRestrictions()
839
                        ->removeAll()
840
                        ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
841
                        ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
842
                    $overlayRecord = $queryBuilder
843
                        ->select('uid')
844
                        ->from('pages')
845
                        ->where(
846
                            $queryBuilder->expr()->eq(
847
                                $localizationParentField,
848
                                $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
849
                            ),
850
                            $queryBuilder->expr()->eq(
851
                                $languageField,
852
                                $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
853
                            )
854
                        )
855
                        ->setMaxResults(1)
856
                        ->execute()
857
                        ->fetch();
858
                    // Edit button
859
                    $urlParameters = [
860
                        'edit' => [
861
                            'pages' => [
862
                                $overlayRecord['uid'] => 'edit'
863
                            ]
864
                        ],
865
                        'returnUrl' => $normalizedParams->getRequestUri(),
866
                    ];
867
868
                    $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
869
                    $editLanguageButton = $this->buttonBar->makeLinkButton()
870
                        ->setHref($url)
871
                        ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
872
                        ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
873
                    $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
874
                }
875
                $urlParameters = [
876
                    'edit' => [
877
                        'pages' => [
878
                            $this->id => 'edit'
879
                        ]
880
                    ],
881
                    'returnUrl' => $normalizedParams->getRequestUri(),
882
                ];
883
                $url = (string)$this->uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
884
                $editPageButton = $this->buttonBar->makeLinkButton()
885
                    ->setHref($url)
886
                    ->setTitle($lang->getLL('editPageProperties'))
887
                    ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
888
                $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
889
            }
890
        }
891
    }
892
893
    /*******************************
894
     *
895
     * Other functions
896
     *
897
     ******************************/
898
    /**
899
     * Returns the number of hidden elements (including those hidden by start/end times)
900
     * on the current page (for the current sys_language)
901
     *
902
     * @param array $languageColumns
903
     * @return int
904
     */
905
    protected function getNumberOfHiddenElements(array $languageColumns): int
906
    {
907
        $andWhere = [];
908
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
909
        $queryBuilder->getRestrictions()
910
            ->removeAll()
911
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
912
            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
913
914
        $queryBuilder
915
            ->count('uid')
916
            ->from('tt_content')
917
            ->where(
918
                $queryBuilder->expr()->eq(
919
                    'pid',
920
                    $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
921
                )
922
            );
923
924
        if (!empty($languageColumns)) {
925
            // Multi-language view is active
926
            if ($this->current_sys_language > 0) {
927
                $queryBuilder->andWhere(
928
                    $queryBuilder->expr()->in(
929
                        'sys_language_uid',
930
                        [0, $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)]
931
                    )
932
                );
933
            }
934
        } else {
935
            $queryBuilder->andWhere(
936
                $queryBuilder->expr()->eq(
937
                    'sys_language_uid',
938
                    $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
939
                )
940
            );
941
        }
942
943
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
944
            $andWhere[] = $queryBuilder->expr()->neq(
945
                'hidden',
946
                $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
947
            );
948
        }
949
950
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
951
            $andWhere[] = $queryBuilder->expr()->andX(
952
                $queryBuilder->expr()->neq(
953
                    'starttime',
954
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
955
                ),
956
                $queryBuilder->expr()->gt(
957
                    'starttime',
958
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
959
                )
960
            );
961
        }
962
963
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
964
            $andWhere[] = $queryBuilder->expr()->andX(
965
                $queryBuilder->expr()->neq(
966
                    'endtime',
967
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
968
                ),
969
                $queryBuilder->expr()->lte(
970
                    'endtime',
971
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
972
                )
973
            );
974
        }
975
976
        if (!empty($andWhere)) {
977
            $queryBuilder->andWhere(
978
                $queryBuilder->expr()->orX(...$andWhere)
979
            );
980
        }
981
982
        $count = $queryBuilder
983
            ->execute()
984
            ->fetchColumn(0);
985
986
        return (int)$count;
987
    }
988
989
    /**
990
     * Check if page can be edited by current user
991
     *
992
     * @param int $languageId
993
     * @return bool
994
     */
995
    protected function isPageEditable(int $languageId): bool
996
    {
997
        if ($this->getBackendUser()->isAdmin()) {
998
            return true;
999
        }
1000
1001
        return !$this->pageinfo['editlock']
1002
            && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::PAGE_EDIT)
1003
            && $this->getBackendUser()->checkLanguageAccess($languageId);
1004
    }
1005
1006
    /**
1007
     * Check if content can be edited by current user
1008
     *
1009
     * @param int $languageId
1010
     * @return bool
1011
     */
1012
    protected function isContentEditable(int $languageId): bool
1013
    {
1014
        if ($this->getBackendUser()->isAdmin()) {
1015
            return true;
1016
        }
1017
1018
        return !$this->pageinfo['editlock']
1019
            && $this->getBackendUser()->doesUserHaveAccess($this->pageinfo, Permission::CONTENT_EDIT)
1020
            && $this->getBackendUser()->checkLanguageAccess($languageId);
1021
    }
1022
1023
    /**
1024
     * Returns LanguageService
1025
     *
1026
     * @return LanguageService
1027
     */
1028
    protected function getLanguageService(): LanguageService
1029
    {
1030
        return $GLOBALS['LANG'];
1031
    }
1032
1033
    /**
1034
     * Returns the current BE user.
1035
     *
1036
     * @return BackendUserAuthentication
1037
     */
1038
    protected function getBackendUser(): BackendUserAuthentication
1039
    {
1040
        return $GLOBALS['BE_USER'];
1041
    }
1042
1043
    /**
1044
     * Make the LanguageMenu
1045
     */
1046
    protected function makeLanguageMenu(): void
1047
    {
1048
        if (count($this->MOD_MENU['language']) > 1) {
1049
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1050
            $languageMenu->setIdentifier('languageMenu');
1051
            foreach ($this->MOD_MENU['language'] as $key => $language) {
1052
                $menuItem = $languageMenu
1053
                    ->makeMenuItem()
1054
                    ->setTitle($language)
1055
                    ->setHref((string)$this->uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1056
                if ((int)$this->current_sys_language === $key) {
1057
                    $menuItem->setActive(true);
1058
                }
1059
                $languageMenu->addMenuItem($menuItem);
1060
            }
1061
            $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1062
        }
1063
    }
1064
1065
    /**
1066
     * Returns the target page if visible
1067
     *
1068
     * @param array $targetPage
1069
     *
1070
     * @return array
1071
     */
1072
    protected function getTargetPageIfVisible(array $targetPage): array
1073
    {
1074
        return !(bool)($targetPage['hidden'] ?? false) ? $targetPage : [];
1075
    }
1076
1077
    /**
1078
     * Creates the search box
1079
     *
1080
     * @return string HTML for the search box
1081
     */
1082
    protected function getSearchBox(): string
1083
    {
1084
        if (!$this->getBackendUser()->check('modules', 'web_list')) {
1085
            return '';
1086
        }
1087
        $lang = $this->getLanguageService();
1088
        $listModule = $this->uriBuilder->buildUriFromRoute('web_list', ['id' => $this->id]);
1089
        // Make level selector:
1090
        $opt = [];
1091
1092
        // "New" generation of search levels ... based on TS config
1093
        $config = BackendUtility::getPagesTSconfig($this->id);
1094
        $searchLevelsFromTSconfig = $config['mod.']['web_list.']['searchLevel.']['items.'];
1095
        $searchLevelItems = [];
1096
1097
        // get translated labels for search levels from pagets
1098
        foreach ($searchLevelsFromTSconfig as $keySearchLevel => $labelConfigured) {
1099
            $label = $lang->sL('LLL:' . $labelConfigured);
1100
            if ($label === '') {
1101
                $label = $labelConfigured;
1102
            }
1103
            $searchLevelItems[$keySearchLevel] = $label;
1104
        }
1105
1106
        foreach ($searchLevelItems as $kv => $label) {
1107
            $opt[] = '<option value="' . $kv . '"' . ($kv === 0 ? ' selected="selected"' : '') . '>'
1108
                . htmlspecialchars($label)
1109
                . '</option>';
1110
        }
1111
        $searchLevelLabel = $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.search_levels');
1112
        $searchStringLabel = $lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.searchString');
1113
        $lMenu = '<select class="form-control" name="search_levels" title="' . htmlspecialchars($searchLevelLabel) . '" id="search_levels">' . implode('', $opt) . '</select>';
1114
        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;">
1115
			<form action="' . htmlspecialchars((string)$listModule) . '" method="post">
1116
                <div id="typo3-dblist-search">
1117
                    <div class="panel panel-default">
1118
                        <div class="panel-body">
1119
                            <div class="row">
1120
                                <div class="form-group col-xs-12">
1121
                                    <label for="search_field">' . htmlspecialchars($searchStringLabel) . ': </label>
1122
									<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="" />
1123
                                </div>
1124
                                <div class="form-group col-xs-12 col-sm-6">
1125
									<label for="search_levels">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.search_levels')) . ': </label>
1126
									' . $lMenu . '
1127
                                </div>
1128
                                <div class="form-group col-xs-12 col-sm-6">
1129
									<label for="showLimit">' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.label.limit')) . ': </label>
1130
									<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="" />
1131
                                </div>
1132
                                <div class="form-group col-xs-12">
1133
                                    <div class="form-control-wrap">
1134
                                        <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')) . '">
1135
                                            ' . $this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL)->render() . ' ' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.search')) . '
1136
                                        </button>
1137
                                    </div>
1138
                                </div>
1139
                            </div>
1140
                        </div>
1141
                    </div>
1142
                </div>
1143
            </form>
1144
        </div>';
1145
    }
1146
}
1147