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