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