Passed
Branch master (6c65a4)
by Christian
16:31
created

PageLayoutController::getPageRenderer()   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
namespace TYPO3\CMS\Backend\Controller;
3
4
/*
5
 * This file is part of the TYPO3 CMS project.
6
 *
7
 * It is free software; you can redistribute it and/or modify it under
8
 * the terms of the GNU General Public License, either version 2
9
 * of the License, or any later version.
10
 *
11
 * For the full copyright and license information, please read the
12
 * LICENSE.txt file that was distributed with this source code.
13
 *
14
 * The TYPO3 project - inspiring people to share!
15
 */
16
17
use Psr\Http\Message\ResponseInterface;
18
use Psr\Http\Message\ServerRequestInterface;
19
use TYPO3\CMS\Backend\Module\ModuleLoader;
20
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
21
use TYPO3\CMS\Backend\Template\ModuleTemplate;
22
use TYPO3\CMS\Backend\Utility\BackendUtility;
23
use TYPO3\CMS\Backend\View\BackendLayoutView;
24
use TYPO3\CMS\Backend\View\PageLayoutView;
25
use TYPO3\CMS\Core\Database\ConnectionPool;
26
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
27
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
28
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
29
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
30
use TYPO3\CMS\Core\DataHandling\DataHandler;
31
use TYPO3\CMS\Core\Imaging\Icon;
32
use TYPO3\CMS\Core\Imaging\IconFactory;
33
use TYPO3\CMS\Core\Page\PageRenderer;
34
use TYPO3\CMS\Core\Type\Bitmask\Permission;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Core\Utility\MathUtility;
37
use TYPO3\CMS\Core\Versioning\VersionState;
38
use TYPO3\CMS\Fluid\View\StandaloneView;
39
use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
40
use TYPO3\CMS\Frontend\Page\PageRepository;
41
use TYPO3\CMS\Recordlist\RecordList;
42
43
/**
44
 * Script Class for Web > Layout module
45
 */
46
class PageLayoutController
47
{
48
    /**
49
     * Page Id for which to make the listing
50
     *
51
     * @var int
52
     */
53
    public $id;
54
55
    /**
56
     * Pointer - for browsing list of records.
57
     *
58
     * @var int
59
     */
60
    public $pointer;
61
62
    /**
63
     * Thumbnails or not
64
     *
65
     * @var string
66
     */
67
    public $imagemode;
68
69
    /**
70
     * Search-fields
71
     *
72
     * @var string
73
     */
74
    public $search_field;
75
76
    /**
77
     * Search-levels
78
     *
79
     * @var int
80
     */
81
    public $search_levels;
82
83
    /**
84
     * Show-limit
85
     *
86
     * @var int
87
     */
88
    public $showLimit;
89
90
    /**
91
     * Return URL
92
     *
93
     * @var string
94
     */
95
    public $returnUrl;
96
97
    /**
98
     * Clear-cache flag - if set, clears page cache for current id.
99
     *
100
     * @var bool
101
     */
102
    public $clear_cache;
103
104
    /**
105
     * PopView id - for opening a window with the page
106
     *
107
     * @var bool
108
     */
109
    public $popView;
110
111
    /**
112
     * Page select perms clause
113
     *
114
     * @var string
115
     */
116
    public $perms_clause;
117
118
    /**
119
     * Module TSconfig
120
     *
121
     * @var array
122
     */
123
    public $modTSconfig;
124
125
    /**
126
     * Module shared TSconfig
127
     *
128
     * @var array
129
     */
130
    public $modSharedTSconfig;
131
132
    /**
133
     * Current ids page record
134
     *
135
     * @var array
136
     */
137
    public $pageinfo;
138
139
    /**
140
     * "Pseudo" Description -table name
141
     *
142
     * @var string
143
     */
144
    public $descrTable;
145
146
    /**
147
     * List of column-integers to edit. Is set from TSconfig, default is "1,0,2,3"
148
     *
149
     * @var string
150
     */
151
    public $colPosList;
152
153
    /**
154
     * Flag: If content can be edited or not.
155
     *
156
     * @var bool
157
     */
158
    public $EDIT_CONTENT;
159
160
    /**
161
     * Users permissions integer for this page.
162
     *
163
     * @var int
164
     */
165
    public $CALC_PERMS;
166
167
    /**
168
     * Currently selected language for editing content elements
169
     *
170
     * @var int
171
     */
172
    public $current_sys_language;
173
174
    /**
175
     * Module configuration
176
     *
177
     * @var array
178
     */
179
    public $MCONF = [];
180
181
    /**
182
     * Menu configuration
183
     *
184
     * @var array
185
     */
186
    public $MOD_MENU = [];
187
188
    /**
189
     * Module settings (session variable)
190
     *
191
     * @var array
192
     */
193
    public $MOD_SETTINGS = [];
194
195
    /**
196
     * Module output accumulation
197
     *
198
     * @var string
199
     */
200
    public $content;
201
202
    /**
203
     * List of column-integers accessible to the current BE user.
204
     * Is set from TSconfig, default is $colPosList
205
     *
206
     * @var string
207
     */
208
    public $activeColPosList;
209
210
    /**
211
     * @var string
212
     */
213
    protected $editSelect;
214
215
    /**
216
     * Caches the available languages in a colPos
217
     *
218
     * @var array
219
     */
220
    protected $languagesInColumnCache = [];
221
222
    /**
223
     * @var IconFactory
224
     */
225
    protected $iconFactory;
226
227
    /**
228
     * The name of the module
229
     *
230
     * @var string
231
     */
232
    protected $moduleName = 'web_layout';
233
234
    /**
235
     * @var ModuleTemplate
236
     */
237
    protected $moduleTemplate;
238
239
    /**
240
     * @var ButtonBar
241
     */
242
    protected $buttonBar;
243
244
    /**
245
     * @var string
246
     */
247
    protected $searchContent;
248
249
    /**
250
     * Initializing the module
251
     */
252
    public function init()
253
    {
254
        $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
255
        $this->iconFactory = $this->moduleTemplate->getIconFactory();
256
        $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
257
        $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
258
        // Setting module configuration / page select clause
259
        $this->MCONF['name'] = $this->moduleName;
260
        $this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
261
        // Get session data
262
        $sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
263
        $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
264
        // GPvars:
265
        $this->id = (int)GeneralUtility::_GP('id');
266
        $this->pointer = GeneralUtility::_GP('pointer');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...Utility::_GP('pointer') can also be of type string. However, the property $pointer is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
267
        $this->imagemode = GeneralUtility::_GP('imagemode');
268
        $this->clear_cache = GeneralUtility::_GP('clear_cache');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ity::_GP('clear_cache') can also be of type string. However, the property $clear_cache is declared as type boolean. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
269
        $this->popView = GeneralUtility::_GP('popView');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...Utility::_GP('popView') can also be of type string. However, the property $popView is declared as type boolean. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
270
        $this->search_field = GeneralUtility::_GP('search_field');
271
        $this->search_levels = GeneralUtility::_GP('search_levels');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...y::_GP('search_levels') can also be of type string. However, the property $search_levels is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
272
        $this->showLimit = GeneralUtility::_GP('showLimit');
0 ignored issues
show
Documentation Bug introduced by
It seems like TYPO3\CMS\Core\Utility\G...ility::_GP('showLimit') can also be of type string. However, the property $showLimit is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
273
        $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
274
        $sessionData['search_field'] = $this->search_field;
275
        // Store session data
276
        $this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
277
        // Load page info array:
278
        $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
279
        // Initialize menu
280
        $this->menuConfig();
281
        // Setting sys language from session var:
282
        $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
283
        // CSH / Descriptions:
284
        $this->descrTable = '_MOD_' . $this->moduleName;
285
    }
286
287
    /**
288
     * Initialize menu array
289
     */
290
    public function menuConfig()
291
    {
292
        $lang = $this->getLanguageService();
293
        // MENU-ITEMS:
294
        $this->MOD_MENU = [
295
            'tt_content_showHidden' => '',
296
            'function' => [
297
                1 => $lang->getLL('m_function_1'),
298
                2 => $lang->getLL('m_function_2')
299
            ],
300
            'language' => [
301
                0 => $lang->getLL('m_default')
302
            ]
303
        ];
304
        // initialize page/be_user TSconfig settings
305
        $this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
306
        $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
307
308
        // First, select all localized page records on the current page. Each represents a possibility for a language on the page. Add these to language selector.
309
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
310
        $queryBuilder->getRestrictions()->removeAll();
311
        if ($this->id) {
312
            $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
313
                ->from('sys_language')
314
                ->join(
315
                    'sys_language',
316
                    'pages',
317
                    'pages',
318
                    $queryBuilder->expr()->eq(
319
                        'sys_language.uid',
320
                        $queryBuilder->quoteIdentifier('pages.' . $GLOBALS['TCA']['pages']['ctrl']['languageField'])
321
                    )
322
                )
323
                ->where(
324
                    $queryBuilder->expr()->eq(
325
                        'pages.deleted',
326
                        $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
327
                    ),
328
                    $queryBuilder->expr()->eq(
329
                        'pages.' . $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
330
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
331
                    ),
332
                    $queryBuilder->expr()->orX(
333
                        $queryBuilder->expr()->gte(
334
                            'pages.t3ver_state',
335
                            $queryBuilder->createNamedParameter(
336
                                (string)new VersionState(VersionState::DEFAULT_STATE),
337
                                \PDO::PARAM_INT
338
                            )
339
                        ),
340
                        $queryBuilder->expr()->eq(
341
                            'pages.t3ver_wsid',
342
                            $queryBuilder->createNamedParameter($this->getBackendUser()->workspace, \PDO::PARAM_INT)
343
                        )
344
                    )
345
                )
346
                ->groupBy(
347
                    'pages.' . $GLOBALS['TCA']['pages']['ctrl']['languageField'],
348
                    'sys_language.uid',
349
                    'sys_language.pid',
350
                    'sys_language.tstamp',
351
                    'sys_language.hidden',
352
                    'sys_language.title',
353
                    'sys_language.language_isocode',
354
                    'sys_language.static_lang_isocode',
355
                    'sys_language.flag',
356
                    'sys_language.sorting'
357
                )
358
                ->orderBy('sys_language.sorting');
359
            if (!$this->getBackendUser()->isAdmin()) {
360
                $queryBuilder->andWhere(
361
                    $queryBuilder->expr()->eq(
362
                        'sys_language.hidden',
363
                        $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
364
                    )
365
                );
366
            }
367
            $statement = $queryBuilder->execute();
368
        } else {
369
            $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
370
            $statement = $queryBuilder->select('uid', 'title')
371
                ->from('sys_language')
372
                ->orderBy('sorting')
373
                ->execute();
374
        }
375
        while ($lRow = $statement->fetch()) {
376
            if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
377
                $this->MOD_MENU['language'][$lRow['uid']] = $lRow['title'];
378
            }
379
        }
380
        // Setting alternative default label:
381
        if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
382
            $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
383
        }
384
        // Initialize the avaiable actions
385
        $actions = $this->initActions();
386
        // Clean up settings
387
        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
0 ignored issues
show
Bug introduced by
It seems like TYPO3\CMS\Core\Utility\GeneralUtility::_GP('SET') can also be of type string; however, parameter $CHANGED_SETTINGS of TYPO3\CMS\Backend\Utilit...tility::getModuleData() 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

387
        $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, /** @scrutinizer ignore-type */ GeneralUtility::_GP('SET'), $this->moduleName);
Loading history...
388
        // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
389
        if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
390
            $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
391
        }
392
        // Make action menu from available actions
393
        $this->makeActionMenu($actions);
394
    }
395
396
    /**
397
     * Initializes the available actions this module provides
398
     *
399
     * @return array the available actions
400
     */
401
    protected function initActions()
402
    {
403
        $actions = [
404
            1 => $this->getLanguageService()->getLL('m_function_1'),
405
            2 => $this->getLanguageService()->getLL('m_function_2')
406
        ];
407
        // Find if there are ANY languages at all (and if not, remove the language option from function menu).
408
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
409
        if ($this->getBackendUser()->isAdmin()) {
410
            $queryBuilder->getRestrictions()->removeAll();
411
        }
412
413
        $count = $queryBuilder
414
            ->count('uid')
415
            ->from('sys_language')
416
            ->execute()
417
            ->fetchColumn(0);
418
419
        if (!$count) {
420
            unset($actions['2']);
421
        }
422
        // page/be_user TSconfig blinding of menu-items
423
        $actions = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $actions, 'menu.function');
424
425
        return $actions;
426
    }
427
428
    /**
429
     * This creates the dropdown menu with the different actions this module is able to provide.
430
     * For now they are Columns, Quick Edit and Languages.
431
     *
432
     * @param array $actions array with the available actions
433
     */
434
    protected function makeActionMenu(array $actions)
435
    {
436
        $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
437
        $actionMenu->setIdentifier('actionMenu');
438
        $actionMenu->setLabel('');
439
440
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
441
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
442
443
        $defaultKey = null;
444
        $foundDefaultKey = false;
445
        foreach ($actions as $key => $action) {
446
            $menuItem = $actionMenu
447
                ->makeMenuItem()
448
                ->setTitle($action)
449
                ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
450
451
            if (!$foundDefaultKey) {
452
                $defaultKey = $key;
453
                $foundDefaultKey = true;
454
            }
455
            if ((int)$this->MOD_SETTINGS['function'] === $key) {
456
                $menuItem->setActive(true);
457
                $defaultKey = null;
458
            }
459
            $actionMenu->addMenuItem($menuItem);
460
        }
461
        if (isset($defaultKey)) {
462
            $this->MOD_SETTINGS['function'] = $defaultKey;
463
        }
464
        $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
465
    }
466
467
    /**
468
     * Clears page cache for the current id, $this->id
469
     */
470
    public function clearCache()
471
    {
472
        if ($this->clear_cache && !empty($this->pageinfo)) {
473
            $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
474
            $dataHandler->start([], []);
475
            $dataHandler->clear_cacheCmd($this->id);
476
        }
477
    }
478
479
    /**
480
     * Generate the flashmessages for current pid
481
     *
482
     * @return string HTML content with flashmessages
483
     */
484
    protected function getHeaderFlashMessagesForCurrentPid()
485
    {
486
        $content = '';
487
        $lang = $this->getLanguageService();
488
489
        $view = GeneralUtility::makeInstance(StandaloneView::class);
490
        $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
491
492
        // If page is a folder
493
        if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
494
            $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
495
            $moduleLoader->load($GLOBALS['TBE_MODULES']);
496
            $modules = $moduleLoader->modules;
497
            if (is_array($modules['web']['sub']['list'])) {
498
                $title = $lang->getLL('goToListModule');
499
                $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
500
                $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
501
                $view->assignMultiple([
502
                    'title' => $title,
503
                    'message' => $message,
504
                    'state' => InfoboxViewHelper::STATE_INFO
505
                ]);
506
                $content .= $view->render();
507
            }
508
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
509
            $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
510
            $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
511
            $targetPage = [];
512
513
            if ($this->pageinfo['shortcut'] || $shortcutMode) {
514
                switch ($shortcutMode) {
515
                    case PageRepository::SHORTCUT_MODE_NONE:
516
                        $targetPage = $pageRepository->getPage($this->pageinfo['shortcut']);
517
                        break;
518
                    case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
519
                        $targetPage = reset($pageRepository->getMenu($this->pageinfo['shortcut'] ?: $this->pageinfo['uid']));
520
                        break;
521
                    case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
522
                        $targetPage = $pageRepository->getPage($this->pageinfo['pid']);
523
                        break;
524
                }
525
526
                $message = '';
527
                if ($shortcutMode === PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
528
                    $message .= sprintf($lang->getLL('pageIsRandomInternalLinkMessage'));
529
                } else {
530
                    $linkToPid = $this->local_linkThisScript(['id' => $targetPage['uid']]);
531
                    $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
532
                    $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

532
                    $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars(/** @scrutinizer ignore-type */ $path) . '</a>';
Loading history...
533
                    $message .= sprintf($lang->getLL('pageIsInternalLinkMessage'), $linkedPath);
534
                }
535
536
                $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', $shortcutMode))) . ')';
537
538
                $view->assignMultiple([
539
                    'title' => $this->pageinfo['title'],
540
                    'message' => $message,
541
                    'state' => InfoboxViewHelper::STATE_INFO
542
                ]);
543
                $content .= $view->render();
544
            } else {
545
                if (empty($targetPage) && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
546
                    $view->assignMultiple([
547
                        'title' => $this->pageinfo['title'],
548
                        'message' => $lang->getLL('pageIsMisconfiguredInternalLinkMessage'),
549
                        'state' => InfoboxViewHelper::STATE_ERROR
550
                    ]);
551
                    $content .= $view->render();
552
                }
553
            }
554
        } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
555
            if (empty($this->pageinfo['url'])) {
556
                $view->assignMultiple([
557
                    'title' => $this->pageinfo['title'],
558
                    'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
559
                    'state' => InfoboxViewHelper::STATE_ERROR
560
                ]);
561
                $content .= $view->render();
562
            } else {
563
                $externalUrl = htmlspecialchars(GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo));
564
                if ($externalUrl !== false) {
565
                    $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noopener">' . $externalUrl . '</a>';
566
                    $view->assignMultiple([
567
                        'title' => $this->pageinfo['title'],
568
                        'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
569
                        'state' => InfoboxViewHelper::STATE_INFO
570
                    ]);
571
                    $content .= $view->render();
572
                }
573
            }
574
        }
575
        // If content from different pid is displayed
576
        if ($this->pageinfo['content_from_pid']) {
577
            $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
578
            $linkToPid = $this->local_linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
579
            $title = BackendUtility::getRecordTitle('pages', $contentPage);
580
            $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
581
            $message = sprintf($lang->getLL('content_from_pid_title'), $link);
582
            $view->assignMultiple([
583
                'title' => $title,
584
                'message' => $message,
585
                'state' => InfoboxViewHelper::STATE_INFO
586
            ]);
587
            $content .= $view->render();
588
        } else {
589
            $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
590
            if (!empty($links)) {
591
                $message = sprintf($lang->getLL('content_on_pid_title'), $links);
592
                $view->assignMultiple([
593
                    'title' => '',
594
                    'message' => $message,
595
                    'state' => InfoboxViewHelper::STATE_INFO
596
                ]);
597
                $content .= $view->render();
598
            }
599
        }
600
        return $content;
601
    }
602
603
    /**
604
     * Get all pages with links where the content of a page $pageId is also shown on
605
     *
606
     * @param int $pageId
607
     * @return string
608
     */
609
    protected function getPageLinksWhereContentIsAlsoShownOn($pageId)
610
    {
611
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
612
        $queryBuilder->getRestrictions()->removeAll();
613
        $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
614
        $queryBuilder
615
            ->select('*')
616
            ->from('pages')
617
            ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
618
619
        $links = [];
620
        $rows = $queryBuilder->execute()->fetchAll();
621
        if (!empty($rows)) {
622
            foreach ($rows as $row) {
623
                $linkToPid = $this->local_linkThisScript(['id' => $row['uid']]);
624
                $title = BackendUtility::getRecordTitle('pages', $row);
625
                $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
626
                $links[] = $link;
627
            }
628
        }
629
        return implode(', ', $links);
630
    }
631
632
    /**
633
     * @return string $title
634
     */
635
    protected function getLocalizedPageTitle()
636
    {
637
        if ($this->current_sys_language > 0) {
638
            $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
639
                ->getQueryBuilderForTable('pages');
640
            $queryBuilder->getRestrictions()
641
                ->removeAll()
642
                ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
643
                ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
644
            $localizedPage = $queryBuilder
645
                ->select('*')
646
                ->from('pages')
647
                ->where(
648
                    $queryBuilder->expr()->eq(
649
                        $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
650
                        $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
651
                    ),
652
                    $queryBuilder->expr()->eq(
653
                        $GLOBALS['TCA']['pages']['ctrl']['languageField'],
654
                        $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
655
                    )
656
                )
657
                ->setMaxResults(1)
658
                ->execute()
659
                ->fetch();
660
            BackendUtility::workspaceOL('pages', $localizedPage);
661
            return $localizedPage['title'];
662
        }
663
        return $this->pageinfo['title'];
664
    }
665
666
    /**
667
     * Injects the request object for the current request or subrequest
668
     * As this controller goes only through the main() method, it is rather simple for now
669
     *
670
     * @param ServerRequestInterface $request the current request
671
     * @param ResponseInterface $response
672
     * @return ResponseInterface the response with the content
673
     */
674
    public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
675
    {
676
        $GLOBALS['SOBE'] = $this;
677
        $this->init();
678
        $this->clearCache();
679
        $this->main();
680
        $response->getBody()->write($this->moduleTemplate->renderContent());
681
        return $response;
682
    }
683
684
    /**
685
     * Main function.
686
     * Creates some general objects and calls other functions for the main rendering of module content.
687
     */
688
    public function main()
689
    {
690
        $lang = $this->getLanguageService();
691
        // Access check...
692
        // The page will show only if there is a valid page and if this page may be viewed by the user
693
        $access = is_array($this->pageinfo);
694
        // Content
695
        $content = '';
696
        if ($this->id && $access) {
697
            // Initialize permission settings:
698
            $this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
699
            $this->EDIT_CONTENT = $this->contentIsNotLockedForEditors();
700
701
            $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
702
703
            // override the default jumpToUrl
704
            $this->moduleTemplate->addJavaScriptCode('jumpToUrl', '
705
                function jumpToUrl(URL,formEl) {
706
                    if (document.editform && TBE_EDITOR.isFormChanged)  {   // Check if the function exists... (works in all browsers?)
707
                        if (!TBE_EDITOR.isFormChanged()) {
708
                            window.location.href = URL;
709
                        } else if (formEl) {
710
                            if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
711
                        }
712
                    } else {
713
                        window.location.href = URL;
714
                    }
715
                }
716
            ');
717
718
            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
719
            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
720
721
            $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
722
                if (top.fsMod) {
723
                    top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
724
                    top.fsMod.navFrameHighlightedID["web"] = "pages' . (int)$this->id . '_"+top.fsMod.currentBank; ' . (int)$this->id . ';
725
                }
726
                ' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
727
                function deleteRecord(table,id,url) {   //
728
                    window.location.href = ' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('tce_db') . '&cmd[')
729
                                             . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url);
730
                    return false;
731
                }
732
            ');
733
734
            // Find backend layout / columns
735
            $backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
736
            if (!empty($backendLayout['__colPosList'])) {
737
                $this->colPosList = implode(',', $backendLayout['__colPosList']);
738
            }
739
            // Removing duplicates, if any
740
            $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...
741
            // Accessible columns
742
            if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
743
                $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...
744
                // Match with the list which is present in the colPosList for the current page
745
                if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
746
                    $this->activeColPosList = array_unique(array_intersect(
747
                        $this->activeColPosList,
748
                        $this->colPosList
749
                    ));
750
                }
751
            } else {
752
                $this->activeColPosList = $this->colPosList;
753
            }
754
            $this->activeColPosList = implode(',', $this->activeColPosList);
755
            $this->colPosList = implode(',', $this->colPosList);
756
757
            $content .= $this->getHeaderFlashMessagesForCurrentPid();
758
759
            // Render the primary module content:
760
            if ($this->MOD_SETTINGS['function'] == 1 || $this->MOD_SETTINGS['function'] == 2) {
761
                $content .= '<form action="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->id, 'imagemode' =>  $this->imagemode])) . '" id="PageLayoutController" method="post">';
762
                // Page title
763
                $content .= '<h1 class="t3js-title-inlineedit">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
764
                // All other listings
765
                $content .= $this->renderContent();
766
            }
767
            $content .= '</form>';
768
            $content .= $this->searchContent;
769
            // Setting up the buttons for the docheader
770
            $this->makeButtons();
771
            // @internal: This is an internal hook for compatibility7 only, this hook will be removed without further notice
772
            if ($this->MOD_SETTINGS['function'] != 1 && $this->MOD_SETTINGS['function'] != 2) {
773
                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['renderActionHook'] ?? [] as $hook) {
774
                    $params = [
775
                        'deleteButton' => $this->deleteButton,
0 ignored issues
show
Bug Best Practice introduced by
The property deleteButton does not exist on TYPO3\CMS\Backend\Controller\PageLayoutController. Did you maybe forget to declare it?
Loading history...
776
                        ''
777
                    ];
778
                    $content .= GeneralUtility::callUserFunction($hook, $params, $this);
779
                }
780
            }
781
            // Create LanguageMenu
782
            $this->makeLanguageMenu();
783
        } else {
784
            $this->moduleTemplate->addJavaScriptCode(
785
                'mainJsFunctions',
786
                'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
787
            );
788
            $content .= '<h1>' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '</h1>';
789
            $view = GeneralUtility::makeInstance(StandaloneView::class);
790
            $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
791
            $view->assignMultiple([
792
                'title' => $lang->getLL('clickAPage_header'),
793
                'message' => $lang->getLL('clickAPage_content'),
794
                'state' => InfoboxViewHelper::STATE_INFO
795
            ]);
796
            $content .= $view->render();
797
        }
798
        // Set content
799
        $this->moduleTemplate->setContent($content);
800
    }
801
802
    /**
803
     * Rendering content
804
     *
805
     * @return string
806
     */
807
    public function renderContent()
808
    {
809
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
810
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
811
812
        $this->moduleTemplate->getPageRenderer()->loadJquery();
813
        $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
814
        /** @var $dbList \TYPO3\CMS\Backend\View\PageLayoutView */
815
        $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
816
        $dbList->thumbs = $this->imagemode;
0 ignored issues
show
Documentation Bug introduced by
The property $thumbs was declared of type boolean, but $this->imagemode is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
817
        $dbList->no_noWrap = 1;
818
        $dbList->descrTable = $this->descrTable;
819
        $this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
820
        $dbList->script = (string)$uriBuilder->buildUriFromRoute($this->moduleName);
821
        $dbList->showIcon = 0;
822
        $dbList->setLMargin = 0;
823
        $dbList->doEdit = $this->EDIT_CONTENT;
824
        $dbList->ext_CALC_PERMS = $this->CALC_PERMS;
825
        $dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears');
826
        $dbList->id = $this->id;
827
        $dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
828
        $dbList->option_newWizard = empty($this->modTSconfig['properties']['disableNewContentElementWizard']);
829
        $dbList->defLangBinding = !empty($this->modTSconfig['properties']['defLangBinding']);
830
        if (!$dbList->nextThree) {
831
            $dbList->nextThree = 1;
832
        }
833
        // Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
834
        // also fills $dbList->activeTables
835
        $dbList->getTableMenu($this->id);
836
        // Initialize other variables:
837
        $tableOutput = [];
838
        $tableJSOutput = [];
839
        $CMcounter = 0;
840
        // Traverse the list of table names which has records on this page (that array is populated
841
        // by the $dblist object during the function getTableMenu()):
842
        foreach ($dbList->activeTables as $table => $value) {
843
            $h_func = '';
844
            $h_func_b = '';
845
            if (!isset($dbList->externalTables[$table])) {
846
                // Toggle hidden ContentElements
847
                $numberOfHiddenElements = $this->getNumberOfHiddenElements();
848
                if ($numberOfHiddenElements > 0) {
849
                    $h_func_b = '
850
                        <div class="checkbox">
851
                            <label for="checkTt_content_showHidden">
852
                                <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
853
                                ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
854
                            </label>
855
                        </div>';
856
                }
857
858
                // Boolean: Display up/down arrows and edit icons for tt_content records
859
                $dbList->tt_contentConfig['showCommands'] = 1;
860
                // Boolean: Display info-marks or not
861
                $dbList->tt_contentConfig['showInfo'] = 1;
862
                // Setting up the tt_content columns to show:
863
                if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
864
                    $colList = [];
865
                    $tcaItems = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this);
866
                    foreach ($tcaItems as $temp) {
867
                        $colList[] = $temp[1];
868
                    }
869
                } else {
870
                    // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
871
                    $colList = ['1', '0', '2', '3'];
872
                }
873
                if ($this->colPosList !== '') {
874
                    $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
875
                }
876
                // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
877
                $dbList->tt_contentConfig['cols'] = implode(',', $colList);
878
                $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
879
                $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
880
                $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
881
                // If the function menu is set to "Language":
882
                if ($this->MOD_SETTINGS['function'] == 2) {
883
                    $dbList->tt_contentConfig['languageMode'] = 1;
884
                    $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
885
                    $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
886
                }
887
            } else {
888
                if (isset($this->MOD_SETTINGS) && isset($this->MOD_MENU)) {
889
                    $h_func = BackendUtility::getFuncMenu($this->id, 'SET[' . $table . ']', $this->MOD_SETTINGS[$table], $this->MOD_MENU[$table], '', '');
890
                }
891
            }
892
            // Start the dblist object:
893
            $dbList->itemsLimitSingleTable = 1000;
894
            $dbList->start($this->id, $table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
895
            $dbList->counter = $CMcounter;
896
            $dbList->ext_function = $this->MOD_SETTINGS['function'];
897
            // Generate the list of elements here:
898
            $dbList->generateList();
899
            // Adding the list content to the tableOutput variable:
900
            $tableOutput[$table] = $h_func . $dbList->HTMLcode . $h_func_b;
901
            // ... and any accumulated JavaScript goes the same way!
902
            $tableJSOutput[$table] = $dbList->JScode;
903
            // Increase global counter:
904
            $CMcounter += $dbList->counter;
905
            // Reset variables after operation:
906
            $dbList->HTMLcode = '';
907
            $dbList->JScode = '';
908
        }
909
        // END: traverse tables
910
        // For Context Sensitive Menus:
911
        // Init the content
912
        $content = '';
913
        // Additional header content
914
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'] ?? [] as $hook) {
915
            $params = [];
916
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
917
        }
918
        // Add the content for each table we have rendered (traversing $tableOutput variable)
919
        foreach ($tableOutput as $table => $output) {
920
            $content .= $output;
921
        }
922
        // Making search form:
923
        if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dbList->counter > 0 || $this->currentPageHasSubPages())) {
924
            $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
925
            $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
926
                ->setClasses('t3js-toggle-search-toolbox')
927
                ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
928
                ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
929
                ->setHref('#');
930
            $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
931
            $this->searchContent = $dbList->getSearchBox();
932
        }
933
        // Additional footer content
934
        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'] ?? [] as $hook) {
935
            $params = [];
936
            $content .= GeneralUtility::callUserFunction($hook, $params, $this);
937
        }
938
        return $content;
939
    }
940
941
    /**
942
     * @return ModuleTemplate
943
     */
944
    public function getModuleTemplate()
945
    {
946
        return $this->moduleTemplate;
947
    }
948
949
    /***************************
950
     *
951
     * Sub-content functions, rendering specific parts of the module content.
952
     *
953
     ***************************/
954
    /**
955
     * This creates the buttons for the modules
956
     */
957
    protected function makeButtons()
958
    {
959
        if ($this->MOD_SETTINGS['function'] == 1 || $this->MOD_SETTINGS['function'] == 2) {
960
            // Add CSH (Context Sensitive Help) icon to tool bar
961
            $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
962
                ->setModuleName($this->descrTable)
963
                ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
964
            $this->buttonBar->addButton($contextSensitiveHelpButton);
965
        }
966
        $lang = $this->getLanguageService();
967
        // View page
968
        if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
969
            $viewButton = $this->buttonBar->makeLinkButton()
970
                ->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))
971
                ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
972
                ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
973
                ->setHref('#');
974
975
            $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
976
        }
977
        // Shortcut
978
        $shortcutButton = $this->buttonBar->makeShortcutButton()
979
            ->setModuleName($this->moduleName)
980
            ->setGetVariables([
981
                'id',
982
                'route',
983
                'edit_record',
984
                'pointer',
985
                'new_unique_uid',
986
                'search_field',
987
                'search_levels',
988
                'showLimit'
989
            ])
990
            ->setSetVariables(array_keys($this->MOD_MENU));
991
        $this->buttonBar->addButton($shortcutButton);
992
993
        /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
994
        $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
995
        // Cache
996
        if (empty($this->modTSconfig['properties']['disableAdvanced'])) {
997
            $clearCacheButton = $this->buttonBar->makeLinkButton()
998
                ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))
999
                ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
1000
                ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
1001
            $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
1002
        }
1003
        if (empty($this->modTSconfig['properties']['disableIconToolbar'])) {
1004
            // Edit page properties and page language overlay icons
1005
            if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
1006
                // Edit localized pages only when one specific language is selected
1007
                if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1008
                    $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
1009
                    $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
1010
                    $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1011
                        ->getQueryBuilderForTable('pages');
1012
                    $queryBuilder->getRestrictions()
1013
                        ->removeAll()
1014
                        ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1015
                        ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1016
                    $overlayRecord = $queryBuilder
1017
                        ->select('uid')
1018
                        ->from('pages')
1019
                        ->where(
1020
                            $queryBuilder->expr()->eq(
1021
                                $localizationParentField,
1022
                                $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1023
                            ),
1024
                            $queryBuilder->expr()->eq(
1025
                                $languageField,
1026
                                $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1027
                            )
1028
                        )
1029
                        ->setMaxResults(1)
1030
                        ->execute()
1031
                        ->fetch();
1032
                    // Edit button
1033
                    $urlParameters = [
1034
                        'edit' => [
1035
                            'pages' => [
1036
                                $overlayRecord['uid'] => 'edit'
1037
                            ]
1038
                        ],
1039
                        'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1040
                    ];
1041
1042
                    $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1043
                    $editLanguageButton = $this->buttonBar->makeLinkButton()
1044
                        ->setHref($url)
1045
                        ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
1046
                        ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
1047
                    $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1048
                }
1049
                $urlParameters = [
1050
                    'edit' => [
1051
                        'pages' => [
1052
                            $this->id => 'edit'
1053
                        ]
1054
                    ],
1055
                    'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1056
                ];
1057
                $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1058
                $editPageButton = $this->buttonBar->makeLinkButton()
1059
                    ->setHref($url)
1060
                    ->setTitle($lang->getLL('editPageProperties'))
1061
                    ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
1062
                $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1063
            }
1064
        }
1065
    }
1066
1067
    /*******************************
1068
     *
1069
     * Other functions
1070
     *
1071
     ******************************/
1072
    /**
1073
     * Returns the number of hidden elements (including those hidden by start/end times)
1074
     * on the current page (for the current sys_language)
1075
     *
1076
     * @return int
1077
     */
1078
    public function getNumberOfHiddenElements()
1079
    {
1080
        /** @var QueryBuilder $queryBuilder */
1081
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
1082
        $queryBuilder->getRestrictions()
1083
            ->removeAll()
1084
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1085
            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1086
1087
        $queryBuilder
1088
            ->count('uid')
1089
            ->from('tt_content')
1090
            ->where(
1091
                $queryBuilder->expr()->eq(
1092
                    'pid',
1093
                    $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1094
                ),
1095
                $queryBuilder->expr()->eq(
1096
                    'sys_language_uid',
1097
                    $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1098
                )
1099
            );
1100
1101
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
1102
            $andWhere[] = $queryBuilder->expr()->neq(
0 ignored issues
show
Comprehensibility Best Practice introduced by
$andWhere was never initialized. Although not strictly required by PHP, it is generally a good practice to add $andWhere = array(); before regardless.
Loading history...
1103
                'hidden',
1104
                $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1105
            );
1106
        }
1107
1108
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
1109
            $andWhere[] = $queryBuilder->expr()->andX(
1110
                $queryBuilder->expr()->neq(
1111
                    'starttime',
1112
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1113
                ),
1114
                $queryBuilder->expr()->gt(
1115
                    'starttime',
1116
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1117
                )
1118
            );
1119
        }
1120
1121
        if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
1122
            $andWhere[] = $queryBuilder->expr()->andX(
1123
                $queryBuilder->expr()->neq(
1124
                    'endtime',
1125
                    $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1126
                ),
1127
                $queryBuilder->expr()->lte(
1128
                    'endtime',
1129
                    $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1130
                )
1131
            );
1132
        }
1133
1134
        if (!empty($andWhere)) {
1135
            $queryBuilder->andWhere(
1136
                $queryBuilder->expr()->orX(...$andWhere)
1137
            );
1138
        }
1139
1140
        $count = $queryBuilder
1141
            ->execute()
1142
            ->fetchColumn(0);
1143
1144
        return (int)$count;
1145
    }
1146
1147
    /**
1148
     * Returns URL to the current script.
1149
     * In particular the "popView" and "new_unique_uid" Get vars are unset.
1150
     *
1151
     * @param array $params Parameters array, merged with global GET vars.
1152
     * @return string URL
1153
     */
1154
    public function local_linkThisScript($params)
1155
    {
1156
        $params['popView'] = '';
1157
        $params['new_unique_uid'] = '';
1158
        return GeneralUtility::linkThisScript($params);
1159
    }
1160
1161
    /**
1162
     * Check if page can be edited by current user
1163
     *
1164
     * @return bool
1165
     */
1166
    public function pageIsNotLockedForEditors()
1167
    {
1168
        return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::PAGE_EDIT) === Permission::PAGE_EDIT && !$this->pageinfo['editlock'];
1169
    }
1170
1171
    /**
1172
     * Check if content can be edited by current user
1173
     *
1174
     * @return bool
1175
     */
1176
    public function contentIsNotLockedForEditors()
1177
    {
1178
        return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::CONTENT_EDIT) === Permission::CONTENT_EDIT && !$this->pageinfo['editlock'];
1179
    }
1180
1181
    /**
1182
     * Returns LanguageService
1183
     *
1184
     * @return \TYPO3\CMS\Core\Localization\LanguageService
1185
     */
1186
    protected function getLanguageService()
1187
    {
1188
        return $GLOBALS['LANG'];
1189
    }
1190
1191
    /**
1192
     * Returns the current BE user.
1193
     *
1194
     * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1195
     */
1196
    protected function getBackendUser()
1197
    {
1198
        return $GLOBALS['BE_USER'];
1199
    }
1200
1201
    /**
1202
     * Returns current PageRenderer
1203
     *
1204
     * @return PageRenderer
1205
     */
1206
    protected function getPageRenderer()
1207
    {
1208
        return GeneralUtility::makeInstance(PageRenderer::class);
1209
    }
1210
1211
    /**
1212
     * Make the LanguageMenu
1213
     */
1214
    protected function makeLanguageMenu()
1215
    {
1216
        if (count($this->MOD_MENU['language']) > 1) {
1217
            /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1218
            $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1219
            $lang = $this->getLanguageService();
0 ignored issues
show
Unused Code introduced by
The assignment to $lang is dead and can be removed.
Loading history...
1220
            $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1221
            $languageMenu->setIdentifier('languageMenu');
1222
            foreach ($this->MOD_MENU['language'] as $key => $language) {
1223
                $menuItem = $languageMenu
1224
                    ->makeMenuItem()
1225
                    ->setTitle($language)
1226
                    ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1227
                if ((int)$this->current_sys_language === $key) {
1228
                    $menuItem->setActive(true);
1229
                }
1230
                $languageMenu->addMenuItem($menuItem);
1231
            }
1232
            $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1233
        }
1234
    }
1235
1236
    /**
1237
     * Checks whether the current page has sub pages
1238
     *
1239
     * @return bool
1240
     */
1241
    protected function currentPageHasSubPages()
1242
    {
1243
        /** @var QueryBuilder $queryBuilder */
1244
        $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1245
        $queryBuilder->getRestrictions()
1246
            ->removeAll()
1247
            ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1248
            ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1249
1250
        // get workspace id
1251
        $workspaceId = (int)$this->getBackendUser()->workspace;
1252
        $comparisonExpression = $workspaceId === 0 ? 'neq' : 'eq';
1253
1254
        $count = $queryBuilder
1255
            ->count('uid')
1256
            ->from('pages')
1257
            ->where(
1258
                $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
1259
                $queryBuilder->expr()->eq(
1260
                    't3ver_wsid',
1261
                    $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
1262
                ),
1263
                $queryBuilder->expr()->{$comparisonExpression}(
1264
                    'pid',
1265
                    $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
1266
                )
1267
            )
1268
            ->execute()
1269
            ->fetchColumn(0);
1270
1271
        return (bool)$count;
1272
    }
1273
}
1274