[BUGFIX] Remember selected page in fsMod and refactor page selection
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / PageLayoutController.php
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\Http\HtmlResponse;
32 use TYPO3\CMS\Core\Imaging\Icon;
33 use TYPO3\CMS\Core\Imaging\IconFactory;
34 use TYPO3\CMS\Core\Page\PageRenderer;
35 use TYPO3\CMS\Core\Type\Bitmask\Permission;
36 use TYPO3\CMS\Core\Utility\GeneralUtility;
37 use TYPO3\CMS\Core\Utility\MathUtility;
38 use TYPO3\CMS\Core\Versioning\VersionState;
39 use TYPO3\CMS\Fluid\View\StandaloneView;
40 use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
41 use TYPO3\CMS\Frontend\Page\PageRepository;
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(__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');
267 $this->imagemode = GeneralUtility::_GP('imagemode');
268 $this->clear_cache = GeneralUtility::_GP('clear_cache');
269 $this->popView = GeneralUtility::_GP('popView');
270 $this->search_field = GeneralUtility::_GP('search_field');
271 $this->search_levels = GeneralUtility::_GP('search_levels');
272 $this->showLimit = GeneralUtility::_GP('showLimit');
273 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
274 $sessionData['search_field'] = $this->search_field;
275 // Store session data
276 $this->getBackendUser()->setAndSaveSessionData(__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 $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
306 $this->modSharedTSconfig['properties'] = $pageTsConfig['mod.']['SHARED.'] ?? [];
307 $this->modTSconfig['properties'] = $pageTsConfig['mod.']['web_layout.'] ?? [];
308
309 // 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.
310 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
311 $queryBuilder->getRestrictions()->removeAll();
312 if ($this->id) {
313 $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
314 ->from('sys_language')
315 ->join(
316 'sys_language',
317 'pages',
318 'pages',
319 $queryBuilder->expr()->eq(
320 'sys_language.uid',
321 $queryBuilder->quoteIdentifier('pages.' . $GLOBALS['TCA']['pages']['ctrl']['languageField'])
322 )
323 )
324 ->where(
325 $queryBuilder->expr()->eq(
326 'pages.deleted',
327 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
328 ),
329 $queryBuilder->expr()->eq(
330 'pages.' . $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
331 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
332 ),
333 $queryBuilder->expr()->orX(
334 $queryBuilder->expr()->gte(
335 'pages.t3ver_state',
336 $queryBuilder->createNamedParameter(
337 (string)new VersionState(VersionState::DEFAULT_STATE),
338 \PDO::PARAM_INT
339 )
340 ),
341 $queryBuilder->expr()->eq(
342 'pages.t3ver_wsid',
343 $queryBuilder->createNamedParameter($this->getBackendUser()->workspace, \PDO::PARAM_INT)
344 )
345 )
346 )
347 ->groupBy(
348 'pages.' . $GLOBALS['TCA']['pages']['ctrl']['languageField'],
349 'sys_language.uid',
350 'sys_language.pid',
351 'sys_language.tstamp',
352 'sys_language.hidden',
353 'sys_language.title',
354 'sys_language.language_isocode',
355 'sys_language.static_lang_isocode',
356 'sys_language.flag',
357 'sys_language.sorting'
358 )
359 ->orderBy('sys_language.sorting');
360 if (!$this->getBackendUser()->isAdmin()) {
361 $queryBuilder->andWhere(
362 $queryBuilder->expr()->eq(
363 'sys_language.hidden',
364 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
365 )
366 );
367 }
368 $statement = $queryBuilder->execute();
369 } else {
370 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
371 $statement = $queryBuilder->select('uid', 'title')
372 ->from('sys_language')
373 ->orderBy('sorting')
374 ->execute();
375 }
376 while ($lRow = $statement->fetch()) {
377 if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
378 $this->MOD_MENU['language'][$lRow['uid']] = $lRow['title'];
379 }
380 }
381 // Setting alternative default label:
382 if ((!empty($this->modSharedTSconfig['properties']['defaultLanguageLabel']) || !empty($this->modTSconfig['properties']['defaultLanguageLabel'])) && isset($this->MOD_MENU['language'][0])) {
383 $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
384 }
385 // Initialize the avaiable actions
386 $actions = $this->initActions();
387 // Clean up settings
388 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
389 // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
390 if ($this->getBackendUser()->workspace != 0
391 || !isset($this->MOD_SETTINGS['tt_content_showHidden'])
392 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0'
393 ) {
394 $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
395 }
396 // Make action menu from available actions
397 $this->makeActionMenu($actions);
398 }
399
400 /**
401 * Initializes the available actions this module provides
402 *
403 * @return array the available actions
404 */
405 protected function initActions()
406 {
407 $actions = [
408 1 => $this->getLanguageService()->getLL('m_function_1'),
409 2 => $this->getLanguageService()->getLL('m_function_2')
410 ];
411 // Find if there are ANY languages at all (and if not, remove the language option from function menu).
412 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
413 if ($this->getBackendUser()->isAdmin()) {
414 $queryBuilder->getRestrictions()->removeAll();
415 }
416
417 $count = $queryBuilder
418 ->count('uid')
419 ->from('sys_language')
420 ->execute()
421 ->fetchColumn(0);
422
423 if (!$count) {
424 unset($actions['2']);
425 }
426 // Page / user TSconfig blinding of menu-items
427 $blindActions = $this->modTSconfig['properties']['menu.']['functions.'] ?? [];
428 foreach ($blindActions as $key => $value) {
429 if (!$value && array_key_exists($key, $actions)) {
430 unset($actions[$key]);
431 }
432 }
433
434 return $actions;
435 }
436
437 /**
438 * This creates the dropdown menu with the different actions this module is able to provide.
439 * For now they are Columns, Quick Edit and Languages.
440 *
441 * @param array $actions array with the available actions
442 */
443 protected function makeActionMenu(array $actions)
444 {
445 $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
446 $actionMenu->setIdentifier('actionMenu');
447 $actionMenu->setLabel('');
448
449 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
450 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
451
452 $defaultKey = null;
453 $foundDefaultKey = false;
454 foreach ($actions as $key => $action) {
455 $menuItem = $actionMenu
456 ->makeMenuItem()
457 ->setTitle($action)
458 ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
459
460 if (!$foundDefaultKey) {
461 $defaultKey = $key;
462 $foundDefaultKey = true;
463 }
464 if ((int)$this->MOD_SETTINGS['function'] === $key) {
465 $menuItem->setActive(true);
466 $defaultKey = null;
467 }
468 $actionMenu->addMenuItem($menuItem);
469 }
470 if (isset($defaultKey)) {
471 $this->MOD_SETTINGS['function'] = $defaultKey;
472 }
473 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
474 }
475
476 /**
477 * Clears page cache for the current id, $this->id
478 */
479 public function clearCache()
480 {
481 if ($this->clear_cache && !empty($this->pageinfo)) {
482 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
483 $dataHandler->start([], []);
484 $dataHandler->clear_cacheCmd($this->id);
485 }
486 }
487
488 /**
489 * Generate the flashmessages for current pid
490 *
491 * @return string HTML content with flashmessages
492 */
493 protected function getHeaderFlashMessagesForCurrentPid()
494 {
495 $content = '';
496 $lang = $this->getLanguageService();
497
498 $view = GeneralUtility::makeInstance(StandaloneView::class);
499 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
500
501 // If page is a folder
502 if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
503 $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
504 $moduleLoader->load($GLOBALS['TBE_MODULES']);
505 $modules = $moduleLoader->modules;
506 if (is_array($modules['web']['sub']['list'])) {
507 $title = $lang->getLL('goToListModule');
508 $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
509 $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
510 $view->assignMultiple([
511 'title' => $title,
512 'message' => $message,
513 'state' => InfoboxViewHelper::STATE_INFO
514 ]);
515 $content .= $view->render();
516 }
517 } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
518 $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
519 $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
520 $targetPage = [];
521
522 if ($this->pageinfo['shortcut'] || $shortcutMode) {
523 switch ($shortcutMode) {
524 case PageRepository::SHORTCUT_MODE_NONE:
525 $targetPage = $pageRepository->getPage($this->pageinfo['shortcut']);
526 break;
527 case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
528 $targetPage = reset($pageRepository->getMenu($this->pageinfo['shortcut'] ?: $this->pageinfo['uid']));
529 break;
530 case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
531 $targetPage = $pageRepository->getPage($this->pageinfo['pid']);
532 break;
533 }
534
535 $message = '';
536 if ($shortcutMode === PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
537 $message .= sprintf($lang->getLL('pageIsRandomInternalLinkMessage'));
538 } else {
539 $linkToPid = $this->local_linkThisScript(['id' => $targetPage['uid']]);
540 $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
541 $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($path) . '</a>';
542 $message .= sprintf($lang->getLL('pageIsInternalLinkMessage'), $linkedPath);
543 }
544
545 $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', $shortcutMode))) . ')';
546
547 $view->assignMultiple([
548 'title' => $this->pageinfo['title'],
549 'message' => $message,
550 'state' => InfoboxViewHelper::STATE_INFO
551 ]);
552 $content .= $view->render();
553 } else {
554 if (empty($targetPage) && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
555 $view->assignMultiple([
556 'title' => $this->pageinfo['title'],
557 'message' => $lang->getLL('pageIsMisconfiguredInternalLinkMessage'),
558 'state' => InfoboxViewHelper::STATE_ERROR
559 ]);
560 $content .= $view->render();
561 }
562 }
563 } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
564 if (empty($this->pageinfo['url'])) {
565 $view->assignMultiple([
566 'title' => $this->pageinfo['title'],
567 'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
568 'state' => InfoboxViewHelper::STATE_ERROR
569 ]);
570 $content .= $view->render();
571 } else {
572 $externalUrl = htmlspecialchars(GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo));
573 if ($externalUrl !== false) {
574 $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noopener">' . $externalUrl . '</a>';
575 $view->assignMultiple([
576 'title' => $this->pageinfo['title'],
577 'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
578 'state' => InfoboxViewHelper::STATE_INFO
579 ]);
580 $content .= $view->render();
581 }
582 }
583 }
584 // If content from different pid is displayed
585 if ($this->pageinfo['content_from_pid']) {
586 $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
587 $linkToPid = $this->local_linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
588 $title = BackendUtility::getRecordTitle('pages', $contentPage);
589 $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
590 $message = sprintf($lang->getLL('content_from_pid_title'), $link);
591 $view->assignMultiple([
592 'title' => $title,
593 'message' => $message,
594 'state' => InfoboxViewHelper::STATE_INFO
595 ]);
596 $content .= $view->render();
597 } else {
598 $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
599 if (!empty($links)) {
600 $message = sprintf($lang->getLL('content_on_pid_title'), $links);
601 $view->assignMultiple([
602 'title' => '',
603 'message' => $message,
604 'state' => InfoboxViewHelper::STATE_INFO
605 ]);
606 $content .= $view->render();
607 }
608 }
609 return $content;
610 }
611
612 /**
613 * Get all pages with links where the content of a page $pageId is also shown on
614 *
615 * @param int $pageId
616 * @return string
617 */
618 protected function getPageLinksWhereContentIsAlsoShownOn($pageId)
619 {
620 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
621 $queryBuilder->getRestrictions()->removeAll();
622 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
623 $queryBuilder
624 ->select('*')
625 ->from('pages')
626 ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
627
628 $links = [];
629 $rows = $queryBuilder->execute()->fetchAll();
630 if (!empty($rows)) {
631 foreach ($rows as $row) {
632 $linkToPid = $this->local_linkThisScript(['id' => $row['uid']]);
633 $title = BackendUtility::getRecordTitle('pages', $row);
634 $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
635 $links[] = $link;
636 }
637 }
638 return implode(', ', $links);
639 }
640
641 /**
642 * @return string $title
643 * @internal
644 */
645 public function getLocalizedPageTitle()
646 {
647 if ($this->current_sys_language > 0) {
648 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
649 ->getQueryBuilderForTable('pages');
650 $queryBuilder->getRestrictions()
651 ->removeAll()
652 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
653 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
654 $localizedPage = $queryBuilder
655 ->select('*')
656 ->from('pages')
657 ->where(
658 $queryBuilder->expr()->eq(
659 $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'],
660 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
661 ),
662 $queryBuilder->expr()->eq(
663 $GLOBALS['TCA']['pages']['ctrl']['languageField'],
664 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
665 )
666 )
667 ->setMaxResults(1)
668 ->execute()
669 ->fetch();
670 BackendUtility::workspaceOL('pages', $localizedPage);
671 return $localizedPage['title'];
672 }
673 return $this->pageinfo['title'];
674 }
675
676 /**
677 * Injects the request object for the current request or subrequest
678 * As this controller goes only through the main() method, it is rather simple for now
679 *
680 * @param ServerRequestInterface $request the current request
681 * @return ResponseInterface the response with the content
682 */
683 public function mainAction(ServerRequestInterface $request): ResponseInterface
684 {
685 $GLOBALS['SOBE'] = $this;
686 $this->init();
687 $this->clearCache();
688 $this->main();
689 return new HtmlResponse($this->moduleTemplate->renderContent());
690 }
691
692 /**
693 * Main function.
694 * Creates some general objects and calls other functions for the main rendering of module content.
695 */
696 public function main()
697 {
698 $lang = $this->getLanguageService();
699 // Access check...
700 // The page will show only if there is a valid page and if this page may be viewed by the user
701 $access = is_array($this->pageinfo);
702 // Content
703 $content = '';
704 if ($this->id && $access) {
705 // Initialize permission settings:
706 $this->CALC_PERMS = $this->getBackendUser()->calcPerms($this->pageinfo);
707 $this->EDIT_CONTENT = $this->contentIsNotLockedForEditors();
708
709 $this->moduleTemplate->getDocHeaderComponent()->setMetaInformation($this->pageinfo);
710
711 // override the default jumpToUrl
712 $this->moduleTemplate->addJavaScriptCode('jumpToUrl', '
713 function jumpToUrl(URL,formEl) {
714 if (document.editform && TBE_EDITOR.isFormChanged) { // Check if the function exists... (works in all browsers?)
715 if (!TBE_EDITOR.isFormChanged()) {
716 window.location.href = URL;
717 } else if (formEl) {
718 if (formEl.type=="checkbox") formEl.checked = formEl.checked ? 0 : 1;
719 }
720 } else {
721 window.location.href = URL;
722 }
723 }
724 ');
725
726 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
727 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
728
729 $this->moduleTemplate->addJavaScriptCode('mainJsFunctions', '
730 if (top.fsMod) {
731 top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
732 top.fsMod.navFrameHighlightedID["web"] = top.fsMod.currentBank + "_" + ' . (int)$this->id . ';
733 }
734 ' . ($this->popView ? BackendUtility::viewOnClick($this->id, '', BackendUtility::BEgetRootLine($this->id)) : '') . '
735 function deleteRecord(table,id,url) { //
736 window.location.href = ' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('tce_db') . '&cmd[')
737 . ' + table + "][" + id + "][delete]=1&redirect=" + encodeURIComponent(url);
738 return false;
739 }
740 ');
741
742 // Find backend layout / columns
743 $backendLayout = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getSelectedBackendLayout', $this->id, $this);
744 if (!empty($backendLayout['__colPosList'])) {
745 $this->colPosList = implode(',', $backendLayout['__colPosList']);
746 }
747 // Removing duplicates, if any
748 $this->colPosList = array_unique(GeneralUtility::intExplode(',', $this->colPosList));
749 // Accessible columns
750 if (isset($this->modSharedTSconfig['properties']['colPos_list']) && trim($this->modSharedTSconfig['properties']['colPos_list']) !== '') {
751 $this->activeColPosList = array_unique(GeneralUtility::intExplode(',', trim($this->modSharedTSconfig['properties']['colPos_list'])));
752 // Match with the list which is present in the colPosList for the current page
753 if (!empty($this->colPosList) && !empty($this->activeColPosList)) {
754 $this->activeColPosList = array_unique(array_intersect(
755 $this->activeColPosList,
756 $this->colPosList
757 ));
758 }
759 } else {
760 $this->activeColPosList = $this->colPosList;
761 }
762 $this->activeColPosList = implode(',', $this->activeColPosList);
763 $this->colPosList = implode(',', $this->colPosList);
764
765 $content .= $this->getHeaderFlashMessagesForCurrentPid();
766
767 // Render the primary module content:
768 if ($this->MOD_SETTINGS['function'] == 1 || $this->MOD_SETTINGS['function'] == 2) {
769 $content .= '<form action="' . htmlspecialchars((string)$uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->id, 'imagemode' => $this->imagemode])) . '" id="PageLayoutController" method="post">';
770 // Page title
771 $content .= '<h1 class="t3js-title-inlineedit">' . htmlspecialchars($this->getLocalizedPageTitle()) . '</h1>';
772 // All other listings
773 $content .= $this->renderContent();
774 }
775 $content .= '</form>';
776 $content .= $this->searchContent;
777 // Setting up the buttons for the docheader
778 $this->makeButtons();
779 // @internal: This is an internal hook for compatibility7 only, this hook will be removed without further notice
780 if ($this->MOD_SETTINGS['function'] != 1 && $this->MOD_SETTINGS['function'] != 2) {
781 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['renderActionHook'] ?? [] as $hook) {
782 $params = [
783 'deleteButton' => $this->deleteButton,
784 ''
785 ];
786 $content .= GeneralUtility::callUserFunction($hook, $params, $this);
787 }
788 }
789 // Create LanguageMenu
790 $this->makeLanguageMenu();
791 } else {
792 $this->moduleTemplate->addJavaScriptCode(
793 'mainJsFunctions',
794 'if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';'
795 );
796 $content .= '<h1>' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '</h1>';
797 $view = GeneralUtility::makeInstance(StandaloneView::class);
798 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
799 $view->assignMultiple([
800 'title' => $lang->getLL('clickAPage_header'),
801 'message' => $lang->getLL('clickAPage_content'),
802 'state' => InfoboxViewHelper::STATE_INFO
803 ]);
804 $content .= $view->render();
805 }
806 // Set content
807 $this->moduleTemplate->setContent($content);
808 }
809
810 /**
811 * Rendering content
812 *
813 * @return string
814 */
815 public function renderContent()
816 {
817 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
818 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
819
820 $this->moduleTemplate->getPageRenderer()->loadJquery();
821 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
822 /** @var $dbList \TYPO3\CMS\Backend\View\PageLayoutView */
823 $dbList = GeneralUtility::makeInstance(PageLayoutView::class);
824 $dbList->thumbs = $this->imagemode;
825 $dbList->no_noWrap = 1;
826 $dbList->descrTable = $this->descrTable;
827 $this->pointer = MathUtility::forceIntegerInRange($this->pointer, 0, 100000);
828 $dbList->script = (string)$uriBuilder->buildUriFromRoute($this->moduleName);
829 $dbList->showIcon = 0;
830 $dbList->setLMargin = 0;
831 $dbList->doEdit = $this->EDIT_CONTENT;
832 $dbList->ext_CALC_PERMS = $this->CALC_PERMS;
833 $dbList->agePrefixes = $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.minutesHoursDaysYears');
834 $dbList->id = $this->id;
835 $dbList->nextThree = MathUtility::forceIntegerInRange($this->modTSconfig['properties']['editFieldsAtATime'], 0, 10);
836 $dbList->option_newWizard = empty($this->modTSconfig['properties']['disableNewContentElementWizard']);
837 $dbList->defLangBinding = !empty($this->modTSconfig['properties']['defLangBinding']);
838 if (!$dbList->nextThree) {
839 $dbList->nextThree = 1;
840 }
841 // Create menu for selecting a table to jump to (this is, if more than just pages/tt_content elements are found on the page!)
842 // also fills $dbList->activeTables
843 $dbList->getTableMenu($this->id);
844 // Initialize other variables:
845 $tableOutput = [];
846 $tableJSOutput = [];
847 $CMcounter = 0;
848 // Traverse the list of table names which has records on this page (that array is populated
849 // by the $dblist object during the function getTableMenu()):
850 foreach ($dbList->activeTables as $table => $value) {
851 $h_func = '';
852 $h_func_b = '';
853 if (!isset($dbList->externalTables[$table])) {
854 // Boolean: Display up/down arrows and edit icons for tt_content records
855 $dbList->tt_contentConfig['showCommands'] = 1;
856 // Boolean: Display info-marks or not
857 $dbList->tt_contentConfig['showInfo'] = 1;
858 // Setting up the tt_content columns to show:
859 if (is_array($GLOBALS['TCA']['tt_content']['columns']['colPos']['config']['items'])) {
860 $colList = [];
861 $tcaItems = GeneralUtility::callUserFunction(BackendLayoutView::class . '->getColPosListItemsParsed', $this->id, $this);
862 foreach ($tcaItems as $temp) {
863 $colList[] = $temp[1];
864 }
865 } else {
866 // ... should be impossible that colPos has no array. But this is the fallback should it make any sense:
867 $colList = ['1', '0', '2', '3'];
868 }
869 if ($this->colPosList !== '') {
870 $colList = array_intersect(GeneralUtility::intExplode(',', $this->colPosList), $colList);
871 }
872 // The order of the rows: Default is left(1), Normal(0), right(2), margin(3)
873 $dbList->tt_contentConfig['cols'] = implode(',', $colList);
874 $dbList->tt_contentConfig['activeCols'] = $this->activeColPosList;
875 $dbList->tt_contentConfig['showHidden'] = $this->MOD_SETTINGS['tt_content_showHidden'];
876 $dbList->tt_contentConfig['sys_language_uid'] = (int)$this->current_sys_language;
877 // If the function menu is set to "Language":
878 if ($this->MOD_SETTINGS['function'] == 2) {
879 $dbList->tt_contentConfig['languageMode'] = 1;
880 $dbList->tt_contentConfig['languageCols'] = $this->MOD_MENU['language'];
881 $dbList->tt_contentConfig['languageColsPointer'] = $this->current_sys_language;
882 }
883 // Toggle hidden ContentElements
884 $numberOfHiddenElements = $this->getNumberOfHiddenElements($dbList->tt_contentConfig);
885 if ($numberOfHiddenElements > 0) {
886 $h_func_b = '
887 <div class="checkbox">
888 <label for="checkTt_content_showHidden">
889 <input type="checkbox" id="checkTt_content_showHidden" class="checkbox" name="SET[tt_content_showHidden]" value="1" ' . ($this->MOD_SETTINGS['tt_content_showHidden'] ? 'checked="checked"' : '') . ' />
890 ' . htmlspecialchars($this->getLanguageService()->getLL('hiddenCE')) . ' (<span class="t3js-hidden-counter">' . $numberOfHiddenElements . '</span>)
891 </label>
892 </div>';
893 }
894 } else {
895 if (isset($this->MOD_SETTINGS) && isset($this->MOD_MENU)) {
896 $h_func = BackendUtility::getFuncMenu($this->id, 'SET[' . $table . ']', $this->MOD_SETTINGS[$table], $this->MOD_MENU[$table], '', '');
897 }
898 }
899 // Start the dblist object:
900 $dbList->itemsLimitSingleTable = 1000;
901 $dbList->start($this->id, $table, $this->pointer, $this->search_field, $this->search_levels, $this->showLimit);
902 $dbList->counter = $CMcounter;
903 $dbList->ext_function = $this->MOD_SETTINGS['function'];
904 // Generate the list of elements here:
905 $dbList->generateList();
906 // Adding the list content to the tableOutput variable:
907 $tableOutput[$table] = $h_func . $dbList->HTMLcode . $h_func_b;
908 // ... and any accumulated JavaScript goes the same way!
909 $tableJSOutput[$table] = $dbList->JScode;
910 // Increase global counter:
911 $CMcounter += $dbList->counter;
912 // Reset variables after operation:
913 $dbList->HTMLcode = '';
914 $dbList->JScode = '';
915 }
916 // END: traverse tables
917 // For Context Sensitive Menus:
918 // Init the content
919 $content = '';
920 // Additional header content
921 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'] ?? [] as $hook) {
922 $params = [];
923 $content .= GeneralUtility::callUserFunction($hook, $params, $this);
924 }
925 // Add the content for each table we have rendered (traversing $tableOutput variable)
926 foreach ($tableOutput as $table => $output) {
927 $content .= $output;
928 }
929 // Making search form:
930 if (!$this->modTSconfig['properties']['disableSearchBox'] && ($dbList->counter > 0 || $this->currentPageHasSubPages())) {
931 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ToggleSearchToolbox');
932 $toggleSearchFormButton = $this->buttonBar->makeLinkButton()
933 ->setClasses('t3js-toggle-search-toolbox')
934 ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.title.searchIcon'))
935 ->setIcon($this->iconFactory->getIcon('actions-search', Icon::SIZE_SMALL))
936 ->setHref('#');
937 $this->buttonBar->addButton($toggleSearchFormButton, ButtonBar::BUTTON_POSITION_LEFT, 4);
938 $this->searchContent = $dbList->getSearchBox();
939 }
940 // Additional footer content
941 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawFooterHook'] ?? [] as $hook) {
942 $params = [];
943 $content .= GeneralUtility::callUserFunction($hook, $params, $this);
944 }
945 return $content;
946 }
947
948 /**
949 * @return ModuleTemplate
950 */
951 public function getModuleTemplate()
952 {
953 return $this->moduleTemplate;
954 }
955
956 /***************************
957 *
958 * Sub-content functions, rendering specific parts of the module content.
959 *
960 ***************************/
961 /**
962 * This creates the buttons for the modules
963 */
964 protected function makeButtons()
965 {
966 if ($this->MOD_SETTINGS['function'] == 1 || $this->MOD_SETTINGS['function'] == 2) {
967 // Add CSH (Context Sensitive Help) icon to tool bar
968 $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
969 ->setModuleName($this->descrTable)
970 ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
971 $this->buttonBar->addButton($contextSensitiveHelpButton);
972 }
973 $lang = $this->getLanguageService();
974 // View page
975 if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
976 $viewButton = $this->buttonBar->makeLinkButton()
977 ->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))
978 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
979 ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL))
980 ->setHref('#');
981
982 $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
983 }
984 // Shortcut
985 $shortcutButton = $this->buttonBar->makeShortcutButton()
986 ->setModuleName($this->moduleName)
987 ->setGetVariables([
988 'id',
989 'route',
990 'edit_record',
991 'pointer',
992 'new_unique_uid',
993 'search_field',
994 'search_levels',
995 'showLimit'
996 ])
997 ->setSetVariables(array_keys($this->MOD_MENU));
998 $this->buttonBar->addButton($shortcutButton);
999
1000 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1001 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1002 // Cache
1003 if (empty($this->modTSconfig['properties']['disableAdvanced'])) {
1004 $clearCacheButton = $this->buttonBar->makeLinkButton()
1005 ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))
1006 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
1007 ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
1008 $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
1009 }
1010 if (empty($this->modTSconfig['properties']['disableIconToolbar'])) {
1011 // Edit page properties and page language overlay icons
1012 if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
1013 // Edit localized pages only when one specific language is selected
1014 if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1015 $localizationParentField = $GLOBALS['TCA']['pages']['ctrl']['transOrigPointerField'];
1016 $languageField = $GLOBALS['TCA']['pages']['ctrl']['languageField'];
1017 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1018 ->getQueryBuilderForTable('pages');
1019 $queryBuilder->getRestrictions()
1020 ->removeAll()
1021 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1022 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1023 $overlayRecord = $queryBuilder
1024 ->select('uid')
1025 ->from('pages')
1026 ->where(
1027 $queryBuilder->expr()->eq(
1028 $localizationParentField,
1029 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1030 ),
1031 $queryBuilder->expr()->eq(
1032 $languageField,
1033 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1034 )
1035 )
1036 ->setMaxResults(1)
1037 ->execute()
1038 ->fetch();
1039 // Edit button
1040 $urlParameters = [
1041 'edit' => [
1042 'pages' => [
1043 $overlayRecord['uid'] => 'edit'
1044 ]
1045 ],
1046 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1047 ];
1048
1049 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1050 $editLanguageButton = $this->buttonBar->makeLinkButton()
1051 ->setHref($url)
1052 ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
1053 ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
1054 $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1055 }
1056 $urlParameters = [
1057 'edit' => [
1058 'pages' => [
1059 $this->id => 'edit'
1060 ]
1061 ],
1062 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1063 ];
1064 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
1065 $editPageButton = $this->buttonBar->makeLinkButton()
1066 ->setHref($url)
1067 ->setTitle($lang->getLL('editPageProperties'))
1068 ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
1069 $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1070 }
1071 }
1072 }
1073
1074 /*******************************
1075 *
1076 * Other functions
1077 *
1078 ******************************/
1079 /**
1080 * Returns the number of hidden elements (including those hidden by start/end times)
1081 * on the current page (for the current sys_language)
1082 *
1083 * @param array $contentConfig
1084 * @return int
1085 */
1086 public function getNumberOfHiddenElements(array $contentConfig = [])
1087 {
1088 /** @var QueryBuilder $queryBuilder */
1089 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
1090 $queryBuilder->getRestrictions()
1091 ->removeAll()
1092 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1093 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1094
1095 $queryBuilder
1096 ->count('uid')
1097 ->from('tt_content')
1098 ->where(
1099 $queryBuilder->expr()->eq(
1100 'pid',
1101 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1102 )
1103 );
1104
1105 if (!empty($contentConfig['languageCols']) && is_array($contentConfig['languageCols'])) {
1106 // Multi-language view is active
1107 if ($this->current_sys_language > 0) {
1108 $queryBuilder->andWhere(
1109 $queryBuilder->expr()->in(
1110 'sys_language_uid',
1111 [0, $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)]
1112 )
1113 );
1114 }
1115 } else {
1116 $queryBuilder->andWhere(
1117 $queryBuilder->expr()->eq(
1118 'sys_language_uid',
1119 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1120 )
1121 );
1122 }
1123
1124 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
1125 $andWhere[] = $queryBuilder->expr()->neq(
1126 'hidden',
1127 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1128 );
1129 }
1130
1131 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
1132 $andWhere[] = $queryBuilder->expr()->andX(
1133 $queryBuilder->expr()->neq(
1134 'starttime',
1135 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1136 ),
1137 $queryBuilder->expr()->gt(
1138 'starttime',
1139 $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1140 )
1141 );
1142 }
1143
1144 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
1145 $andWhere[] = $queryBuilder->expr()->andX(
1146 $queryBuilder->expr()->neq(
1147 'endtime',
1148 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1149 ),
1150 $queryBuilder->expr()->lte(
1151 'endtime',
1152 $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1153 )
1154 );
1155 }
1156
1157 if (!empty($andWhere)) {
1158 $queryBuilder->andWhere(
1159 $queryBuilder->expr()->orX(...$andWhere)
1160 );
1161 }
1162
1163 $count = $queryBuilder
1164 ->execute()
1165 ->fetchColumn(0);
1166
1167 return (int)$count;
1168 }
1169
1170 /**
1171 * Returns URL to the current script.
1172 * In particular the "popView" and "new_unique_uid" Get vars are unset.
1173 *
1174 * @param array $params Parameters array, merged with global GET vars.
1175 * @return string URL
1176 */
1177 public function local_linkThisScript($params)
1178 {
1179 $params['popView'] = '';
1180 $params['new_unique_uid'] = '';
1181 return GeneralUtility::linkThisScript($params);
1182 }
1183
1184 /**
1185 * Check if page can be edited by current user
1186 *
1187 * @return bool
1188 */
1189 public function pageIsNotLockedForEditors()
1190 {
1191 return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::PAGE_EDIT) === Permission::PAGE_EDIT && !$this->pageinfo['editlock'];
1192 }
1193
1194 /**
1195 * Check if content can be edited by current user
1196 *
1197 * @return bool
1198 */
1199 public function contentIsNotLockedForEditors()
1200 {
1201 return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::CONTENT_EDIT) === Permission::CONTENT_EDIT && !$this->pageinfo['editlock'];
1202 }
1203
1204 /**
1205 * Returns LanguageService
1206 *
1207 * @return \TYPO3\CMS\Core\Localization\LanguageService
1208 */
1209 protected function getLanguageService()
1210 {
1211 return $GLOBALS['LANG'];
1212 }
1213
1214 /**
1215 * Returns the current BE user.
1216 *
1217 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1218 */
1219 protected function getBackendUser()
1220 {
1221 return $GLOBALS['BE_USER'];
1222 }
1223
1224 /**
1225 * Returns current PageRenderer
1226 *
1227 * @return PageRenderer
1228 */
1229 protected function getPageRenderer()
1230 {
1231 return GeneralUtility::makeInstance(PageRenderer::class);
1232 }
1233
1234 /**
1235 * Make the LanguageMenu
1236 */
1237 protected function makeLanguageMenu()
1238 {
1239 if (count($this->MOD_MENU['language']) > 1) {
1240 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
1241 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
1242 $lang = $this->getLanguageService();
1243 $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1244 $languageMenu->setIdentifier('languageMenu');
1245 foreach ($this->MOD_MENU['language'] as $key => $language) {
1246 $menuItem = $languageMenu
1247 ->makeMenuItem()
1248 ->setTitle($language)
1249 ->setHref((string)$uriBuilder->buildUriFromRoute($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1250 if ((int)$this->current_sys_language === $key) {
1251 $menuItem->setActive(true);
1252 }
1253 $languageMenu->addMenuItem($menuItem);
1254 }
1255 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1256 }
1257 }
1258
1259 /**
1260 * Checks whether the current page has sub pages
1261 *
1262 * @return bool
1263 */
1264 protected function currentPageHasSubPages()
1265 {
1266 /** @var QueryBuilder $queryBuilder */
1267 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1268 $queryBuilder->getRestrictions()
1269 ->removeAll()
1270 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1271 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1272
1273 // get workspace id
1274 $workspaceId = (int)$this->getBackendUser()->workspace;
1275 $comparisonExpression = $workspaceId === 0 ? 'neq' : 'eq';
1276
1277 $count = $queryBuilder
1278 ->count('uid')
1279 ->from('pages')
1280 ->where(
1281 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
1282 $queryBuilder->expr()->eq(
1283 't3ver_wsid',
1284 $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
1285 ),
1286 $queryBuilder->expr()->{$comparisonExpression}(
1287 'pid',
1288 $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
1289 )
1290 )
1291 ->execute()
1292 ->fetchColumn(0);
1293
1294 return (bool)$count;
1295 }
1296 }