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