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