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