086c100595e6e04d2ad17103aade2d8bb0092df0
[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 public function init()
280 {
281 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
282 $this->iconFactory = $this->moduleTemplate->getIconFactory();
283 $this->buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
284 $this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
285 // Setting module configuration / page select clause
286 $this->MCONF['name'] = $this->moduleName;
287 $this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
288 // Get session data
289 $sessionData = $this->getBackendUser()->getSessionData(RecordList::class);
290 $this->search_field = !empty($sessionData['search_field']) ? $sessionData['search_field'] : '';
291 // GPvars:
292 $this->id = (int)GeneralUtility::_GP('id');
293 $this->pointer = GeneralUtility::_GP('pointer');
294 $this->imagemode = GeneralUtility::_GP('imagemode');
295 $this->clear_cache = GeneralUtility::_GP('clear_cache');
296 $this->popView = GeneralUtility::_GP('popView');
297 $this->edit_record = GeneralUtility::_GP('edit_record');
298 $this->new_unique_uid = GeneralUtility::_GP('new_unique_uid');
299 $this->search_field = GeneralUtility::_GP('search_field');
300 $this->search_levels = GeneralUtility::_GP('search_levels');
301 $this->showLimit = GeneralUtility::_GP('showLimit');
302 $this->returnUrl = GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'));
303 $this->externalTables = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables'];
304 $sessionData['search_field'] = $this->search_field;
305 // Store session data
306 $this->getBackendUser()->setAndSaveSessionData(RecordList::class, $sessionData);
307 // Load page info array:
308 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
309 // Initialize menu
310 $this->menuConfig();
311 // Setting sys language from session var:
312 $this->current_sys_language = (int)$this->MOD_SETTINGS['language'];
313 // CSH / Descriptions:
314 $this->descrTable = '_MOD_' . $this->moduleName;
315 }
316
317 /**
318 * Initialize menu array
319 */
320 public function menuConfig()
321 {
322 $lang = $this->getLanguageService();
323 // MENU-ITEMS:
324 $this->MOD_MENU = [
325 'tt_content_showHidden' => '',
326 'function' => [
327 1 => $lang->getLL('m_function_1'),
328 2 => $lang->getLL('m_function_2')
329 ],
330 'language' => [
331 0 => $lang->getLL('m_default')
332 ]
333 ];
334 // initialize page/be_user TSconfig settings
335 $this->modSharedTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.SHARED');
336 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
337 // example settings:
338 // $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['cms']['db_layout']['addTables']['tx_myext'] =
339 // array ('default' => array(
340 // 'MENU' => 'LLL:EXT:tx_myext/locallang_db.xlf:menuDefault',
341 // 'fList' => 'title,description,image',
342 // 'icon' => TRUE));
343 if (is_array($this->externalTables)) {
344 if (!empty($this->externalTables)) {
345 GeneralUtility::deprecationLog(
346 'The rendering of records in the page module by using '
347 . '$GLOBALS[\'TYPO3_CONF_VARS\'][\'EXTCONF\'][\'cms\'][\'db_layout\'][\'addTables\']'
348 . ' has been deprecated since TYPO3 CMS 8 and will be removed in TYPO3 CMS 9.'
349 );
350 }
351 foreach ($this->externalTables as $table => $tableSettings) {
352 // delete the default settings from above
353 if (is_array($this->MOD_MENU[$table])) {
354 unset($this->MOD_MENU[$table]);
355 }
356 if (is_array($tableSettings) && count($tableSettings) > 1) {
357 foreach ($tableSettings as $key => $settings) {
358 $this->MOD_MENU[$table][$key] = $lang->sL($settings['MENU']);
359 }
360 }
361 }
362 }
363 // 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.
364 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
365 $queryBuilder->getRestrictions()->removeAll();
366 if ($this->id) {
367 $queryBuilder->select('sys_language.uid AS uid', 'sys_language.title AS title')
368 ->from('sys_language')
369 ->join(
370 'sys_language',
371 'pages_language_overlay',
372 'pages_language_overlay',
373 $queryBuilder->expr()->eq(
374 'sys_language.uid',
375 $queryBuilder->quoteIdentifier('pages_language_overlay.sys_language_uid')
376 )
377 )
378 ->where(
379 $queryBuilder->expr()->eq(
380 'pages_language_overlay.deleted',
381 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
382 ),
383 $queryBuilder->expr()->eq(
384 'pages_language_overlay.pid',
385 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
386 ),
387 $queryBuilder->expr()->orX(
388 $queryBuilder->expr()->gte(
389 'pages_language_overlay.t3ver_state',
390 $queryBuilder->createNamedParameter(
391 (string)new VersionState(VersionState::DEFAULT_STATE),
392 \PDO::PARAM_INT
393 )
394 ),
395 $queryBuilder->expr()->eq(
396 'pages_language_overlay.t3ver_wsid',
397 $queryBuilder->createNamedParameter($this->getBackendUser()->workspace, \PDO::PARAM_INT)
398 )
399 )
400 )
401 ->groupBy(
402 'pages_language_overlay.sys_language_uid',
403 'sys_language.uid',
404 'sys_language.pid',
405 'sys_language.tstamp',
406 'sys_language.hidden',
407 'sys_language.title',
408 'sys_language.language_isocode',
409 'sys_language.static_lang_isocode',
410 'sys_language.flag',
411 'sys_language.sorting'
412 )
413 ->orderBy('sys_language.sorting');
414 if (!$this->getBackendUser()->isAdmin()) {
415 $queryBuilder->andWhere(
416 $queryBuilder->expr()->eq(
417 'sys_language.hidden',
418 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
419 )
420 );
421 }
422 $statement = $queryBuilder->execute();
423 } else {
424 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(HiddenRestriction::class));
425 $statement = $queryBuilder->select('uid', 'title')
426 ->from('sys_language')
427 ->orderBy('sorting')
428 ->execute();
429 }
430 while ($lRow = $statement->fetch()) {
431 if ($this->getBackendUser()->checkLanguageAccess($lRow['uid'])) {
432 $this->MOD_MENU['language'][$lRow['uid']] = $lRow['title'];
433 }
434 }
435 // Setting alternative default label:
436 if (($this->modSharedTSconfig['properties']['defaultLanguageLabel'] || $this->modTSconfig['properties']['defaultLanguageLabel']) && isset($this->MOD_MENU['language'][0])) {
437 $this->MOD_MENU['language'][0] = $this->modTSconfig['properties']['defaultLanguageLabel'] ? $this->modTSconfig['properties']['defaultLanguageLabel'] : $this->modSharedTSconfig['properties']['defaultLanguageLabel'];
438 }
439 // Initialize the avaiable actions
440 $actions = $this->initActions();
441 // Clean up settings
442 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->moduleName);
443 // For all elements to be shown in draft workspaces & to also show hidden elements by default if user hasn't disabled the option
444 if ($this->getBackendUser()->workspace != 0 || $this->MOD_SETTINGS['tt_content_showHidden'] !== '0') {
445 $this->MOD_SETTINGS['tt_content_showHidden'] = 1;
446 }
447 // Make action menu from available actions
448 $this->makeActionMenu($actions);
449 }
450
451 /**
452 * Initializes the available actions this module provides
453 *
454 * @return array the available actions
455 */
456 protected function initActions()
457 {
458 $actions = [
459 1 => $this->getLanguageService()->getLL('m_function_1'),
460 2 => $this->getLanguageService()->getLL('m_function_2')
461 ];
462 // Find if there are ANY languages at all (and if not, remove the language option from function menu).
463 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_language');
464 if ($this->getBackendUser()->isAdmin()) {
465 $queryBuilder->getRestrictions()->removeAll();
466 }
467
468 $count = $queryBuilder
469 ->count('uid')
470 ->from('sys_language')
471 ->execute()
472 ->fetchColumn(0);
473
474 if (!$count) {
475 unset($actions['2']);
476 }
477 // @internal: This is an internal hook for compatibility7 only, this hook will be removed without further notice
478 $initActionHook = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['initActionHook'];
479 if (is_array($initActionHook)) {
480 foreach ($initActionHook as $hook) {
481 $params = [
482 'actions' => &$actions
483 ];
484 GeneralUtility::callUserFunction($hook, $params, $this);
485 }
486 }
487 // page/be_user TSconfig blinding of menu-items
488 $actions = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $actions, 'menu.function');
489
490 return $actions;
491 }
492
493 /**
494 * This creates the dropdown menu with the different actions this module is able to provide.
495 * For now they are Columns, Quick Edit and Languages.
496 *
497 * @param array $actions array with the available actions
498 */
499 protected function makeActionMenu(array $actions)
500 {
501 $actionMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
502 $actionMenu->setIdentifier('actionMenu');
503 $actionMenu->setLabel('');
504
505 $defaultKey = null;
506 $foundDefaultKey = false;
507 foreach ($actions as $key => $action) {
508 $menuItem = $actionMenu
509 ->makeMenuItem()
510 ->setTitle($action)
511 ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[function]=' . $key);
512
513 if (!$foundDefaultKey) {
514 $defaultKey = $key;
515 $foundDefaultKey = true;
516 }
517 if ((int)$this->MOD_SETTINGS['function'] === $key) {
518 $menuItem->setActive(true);
519 $defaultKey = null;
520 }
521 $actionMenu->addMenuItem($menuItem);
522 }
523 if (isset($defaultKey)) {
524 $this->MOD_SETTINGS['function'] = $defaultKey;
525 }
526 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($actionMenu);
527 }
528
529 /**
530 * Clears page cache for the current id, $this->id
531 */
532 public function clearCache()
533 {
534 if ($this->clear_cache && !empty($this->pageinfo)) {
535 $dataHandler = GeneralUtility::makeInstance(DataHandler::class);
536 $dataHandler->start([], []);
537 $dataHandler->clear_cacheCmd($this->id);
538 }
539 }
540
541 /**
542 * Generate the flashmessages for current pid
543 *
544 * @return string HTML content with flashmessages
545 */
546 protected function getHeaderFlashMessagesForCurrentPid()
547 {
548 $content = '';
549 $lang = $this->getLanguageService();
550
551 $view = GeneralUtility::makeInstance(StandaloneView::class);
552 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/InfoBox.html'));
553
554 // If page is a folder
555 if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
556 $moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
557 $moduleLoader->load($GLOBALS['TBE_MODULES']);
558 $modules = $moduleLoader->modules;
559 if (is_array($modules['web']['sub']['list'])) {
560 $title = $lang->getLL('goToListModule');
561 $message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
562 $message .= '<a class="btn btn-info" href="javascript:top.goToModule(\'web_list\',1);">' . $lang->getLL('goToListModule') . '</a>';
563 $view->assignMultiple([
564 'title' => $title,
565 'message' => $message,
566 'state' => InfoboxViewHelper::STATE_INFO
567 ]);
568 $content .= $view->render();
569 }
570 } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_SHORTCUT) {
571 $shortcutMode = (int)$this->pageinfo['shortcut_mode'];
572 $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
573 $targetPage = [];
574
575 if ($this->pageinfo['shortcut'] || $shortcutMode) {
576 switch ($shortcutMode) {
577 case PageRepository::SHORTCUT_MODE_NONE:
578 $targetPage = $pageRepository->getPage($this->pageinfo['shortcut']);
579 break;
580 case PageRepository::SHORTCUT_MODE_FIRST_SUBPAGE:
581 $targetPage = reset($pageRepository->getMenu($this->pageinfo['shortcut'] ?: $this->pageinfo['uid']));
582 break;
583 case PageRepository::SHORTCUT_MODE_PARENT_PAGE:
584 $targetPage = $pageRepository->getPage($this->pageinfo['pid']);
585 break;
586 }
587
588 $message = '';
589 if ($shortcutMode === PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
590 $message .= sprintf($lang->getLL('pageIsRandomInternalLinkMessage'));
591 } else {
592 $linkToPid = $this->local_linkThisScript(['id' => $targetPage['uid']]);
593 $path = BackendUtility::getRecordPath($targetPage['uid'], $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW), 1000);
594 $linkedPath = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($path) . '</a>';
595 $message .= sprintf($lang->getLL('pageIsInternalLinkMessage'), $linkedPath);
596 }
597
598 $message .= ' (' . htmlspecialchars($lang->sL(BackendUtility::getLabelFromItemlist('pages', 'shortcut_mode', $shortcutMode))) . ')';
599
600 $view->assignMultiple([
601 'title' => $this->pageinfo['title'],
602 'message' => $message,
603 'state' => InfoboxViewHelper::STATE_INFO
604 ]);
605 $content .= $view->render();
606 } else {
607 if (empty($targetPage) && $shortcutMode !== PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE) {
608 $view->assignMultiple([
609 'title' => $this->pageinfo['title'],
610 'message' => $lang->getLL('pageIsMisconfiguredInternalLinkMessage'),
611 'state' => InfoboxViewHelper::STATE_ERROR
612 ]);
613 $content .= $view->render();
614 }
615 }
616 } elseif ($this->pageinfo['doktype'] === PageRepository::DOKTYPE_LINK) {
617 if (empty($this->pageinfo['url'])) {
618 $view->assignMultiple([
619 'title' => $this->pageinfo['title'],
620 'message' => $lang->getLL('pageIsMisconfiguredExternalLinkMessage'),
621 'state' => InfoboxViewHelper::STATE_ERROR
622 ]);
623 $content .= $view->render();
624 } else {
625 $externalUrl = htmlspecialchars(GeneralUtility::makeInstance(PageRepository::class)->getExtURL($this->pageinfo));
626 if ($externalUrl !== false) {
627 $externalUrlHtml = '<a href="' . $externalUrl . '" target="_blank" rel="noopener">' . $externalUrl . '</a>';
628 $view->assignMultiple([
629 'title' => $this->pageinfo['title'],
630 'message' => sprintf($lang->getLL('pageIsExternalLinkMessage'), $externalUrlHtml),
631 'state' => InfoboxViewHelper::STATE_INFO
632 ]);
633 $content .= $view->render();
634 }
635 }
636 }
637 // If content from different pid is displayed
638 if ($this->pageinfo['content_from_pid']) {
639 $contentPage = BackendUtility::getRecord('pages', (int)$this->pageinfo['content_from_pid']);
640 $linkToPid = $this->local_linkThisScript(['id' => $this->pageinfo['content_from_pid']]);
641 $title = BackendUtility::getRecordTitle('pages', $contentPage);
642 $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$this->pageinfo['content_from_pid'] . ')</a>';
643 $message = sprintf($lang->getLL('content_from_pid_title'), $link);
644 $view->assignMultiple([
645 'title' => $title,
646 'message' => $message,
647 'state' => InfoboxViewHelper::STATE_INFO
648 ]);
649 $content .= $view->render();
650 } else {
651 $links = $this->getPageLinksWhereContentIsAlsoShownOn($this->pageinfo['uid']);
652 if (!empty($links)) {
653 $message = sprintf($lang->getLL('content_on_pid_title'), $links);
654 $view->assignMultiple([
655 'title' => '',
656 'message' => $message,
657 'state' => InfoboxViewHelper::STATE_INFO
658 ]);
659 $content .= $view->render();
660 }
661 }
662 return $content;
663 }
664
665 /**
666 * Get all pages with links where the content of a page $pageId is also shown on
667 *
668 * @param int $pageId
669 * @return string
670 */
671 protected function getPageLinksWhereContentIsAlsoShownOn($pageId)
672 {
673 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
674 $queryBuilder->getRestrictions()->removeAll();
675 $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
676 $queryBuilder
677 ->select('*')
678 ->from('pages')
679 ->where($queryBuilder->expr()->eq('content_from_pid', $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)));
680
681 $links = [];
682 $rows = $queryBuilder->execute()->fetchAll();
683 if (!empty($rows)) {
684 foreach ($rows as $row) {
685 $linkToPid = $this->local_linkThisScript(['id' => $row['uid']]);
686 $title = BackendUtility::getRecordTitle('pages', $row);
687 $link = '<a href="' . htmlspecialchars($linkToPid) . '">' . htmlspecialchars($title) . ' (PID ' . (int)$row['uid'] . ')</a>';
688 $links[] = $link;
689 }
690 }
691 return implode(', ', $links);
692 }
693
694 /**
695 * @return string $title
696 */
697 protected function getLocalizedPageTitle()
698 {
699 if ($this->current_sys_language > 0) {
700 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
701 ->getQueryBuilderForTable('pages_language_overlay');
702 $queryBuilder->getRestrictions()
703 ->removeAll()
704 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
705 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
706 $overlayRecord = $queryBuilder
707 ->select('*')
708 ->from('pages_language_overlay')
709 ->where(
710 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
711 $queryBuilder->expr()->eq(
712 'sys_language_uid',
713 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
714 )
715 )
716 ->setMaxResults(1)
717 ->execute()
718 ->fetch();
719 BackendUtility::workspaceOL('pages_language_overlay', $overlayRecord);
720 return $overlayRecord['title'];
721 }
722 return $this->pageinfo['title'];
723 }
724
725 /**
726 * Injects the request object for the current request or subrequest
727 * As this controller goes only through the main() method, it is rather simple for now
728 *
729 * @param ServerRequestInterface $request the current request
730 * @param ResponseInterface $response
731 * @return ResponseInterface the response with the content
732 */
733 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
734 {
735 $GLOBALS['SOBE'] = $this;
736 $this->init();
737 $this->clearCache();
738 $this->main();
739 $response->getBody()->write($this->moduleTemplate->renderContent());
740 return $response;
741 }
742
743 /**
744 * Main function.
745 * Creates some general objects and calls other functions for the main rendering of module content.
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 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
1017 */
1018 public function printContent()
1019 {
1020 GeneralUtility::logDeprecatedFunction();
1021 echo $this->moduleTemplate->renderContent();
1022 }
1023
1024 /***************************
1025 *
1026 * Sub-content functions, rendering specific parts of the module content.
1027 *
1028 ***************************/
1029 /**
1030 * This creates the buttons for the modules
1031 */
1032 protected function makeButtons()
1033 {
1034 if ($this->MOD_SETTINGS['function'] == 1 || $this->MOD_SETTINGS['function'] == 2) {
1035 // Add CSH (Context Sensitive Help) icon to tool bar
1036 $contextSensitiveHelpButton = $this->buttonBar->makeHelpButton()
1037 ->setModuleName($this->descrTable)
1038 ->setFieldName('columns_' . $this->MOD_SETTINGS['function']);
1039 $this->buttonBar->addButton($contextSensitiveHelpButton);
1040 }
1041 $lang = $this->getLanguageService();
1042 // View page
1043 if (!VersionState::cast($this->pageinfo['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
1044 $viewButton = $this->buttonBar->makeLinkButton()
1045 ->setOnClick(BackendUtility::viewOnClick($this->pageinfo['uid'], '', BackendUtility::BEgetRootLine($this->pageinfo['uid'])))
1046 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
1047 ->setIcon($this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL))
1048 ->setHref('#');
1049
1050 $this->buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1051 }
1052 // Shortcut
1053 $shortcutButton = $this->buttonBar->makeShortcutButton()
1054 ->setModuleName($this->moduleName)
1055 ->setGetVariables([
1056 'id',
1057 'M',
1058 'edit_record',
1059 'pointer',
1060 'new_unique_uid',
1061 'search_field',
1062 'search_levels',
1063 'showLimit'
1064 ])
1065 ->setSetVariables(array_keys($this->MOD_MENU));
1066 $this->buttonBar->addButton($shortcutButton);
1067
1068 // Cache
1069 if (!$this->modTSconfig['properties']['disableAdvanced']) {
1070 $clearCacheButton = $this->buttonBar->makeLinkButton()
1071 ->setHref(BackendUtility::getModuleUrl($this->moduleName, ['id' => $this->pageinfo['uid'], 'clear_cache' => '1']))
1072 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.clear_cache'))
1073 ->setIcon($this->iconFactory->getIcon('actions-system-cache-clear', Icon::SIZE_SMALL));
1074 $this->buttonBar->addButton($clearCacheButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
1075 }
1076 if (!$this->modTSconfig['properties']['disableIconToolbar']) {
1077 // Edit page properties and page language overlay icons
1078 if ($this->pageIsNotLockedForEditors() && $this->getBackendUser()->checkLanguageAccess(0)) {
1079 // Edit localized page_language_overlay only when one specific language is selected
1080 if ($this->MOD_SETTINGS['function'] == 1 && $this->current_sys_language > 0) {
1081 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1082 ->getQueryBuilderForTable('pages_language_overlay');
1083 $queryBuilder->getRestrictions()
1084 ->removeAll()
1085 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1086 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1087 $overlayRecord = $queryBuilder
1088 ->select('uid')
1089 ->from('pages_language_overlay')
1090 ->where(
1091 $queryBuilder->expr()->eq(
1092 'pid',
1093 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1094 ),
1095 $queryBuilder->expr()->eq(
1096 'sys_language_uid',
1097 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1098 )
1099 )
1100 ->setMaxResults(1)
1101 ->execute()
1102 ->fetch();
1103 // Edit button
1104 $urlParameters = [
1105 'edit' => [
1106 'pages_language_overlay' => [
1107 $overlayRecord['uid'] => 'edit'
1108 ]
1109 ],
1110 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1111 ];
1112 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1113 $editLanguageButton = $this->buttonBar->makeLinkButton()
1114 ->setHref($url)
1115 ->setTitle($lang->getLL('editPageLanguageOverlayProperties'))
1116 ->setIcon($this->iconFactory->getIcon('mimetypes-x-content-page-language-overlay', Icon::SIZE_SMALL));
1117 $this->buttonBar->addButton($editLanguageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1118 }
1119 $urlParameters = [
1120 'edit' => [
1121 'pages' => [
1122 $this->id => 'edit'
1123 ]
1124 ],
1125 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
1126 ];
1127 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
1128 $editPageButton = $this->buttonBar->makeLinkButton()
1129 ->setHref($url)
1130 ->setTitle($lang->getLL('editPageProperties'))
1131 ->setIcon($this->iconFactory->getIcon('actions-page-open', Icon::SIZE_SMALL));
1132 $this->buttonBar->addButton($editPageButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
1133 }
1134 }
1135 }
1136
1137 /*******************************
1138 *
1139 * Other functions
1140 *
1141 ******************************/
1142 /**
1143 * Returns the number of hidden elements (including those hidden by start/end times)
1144 * on the current page (for the current sys_language)
1145 *
1146 * @return int
1147 */
1148 public function getNumberOfHiddenElements()
1149 {
1150 /** @var QueryBuilder $queryBuilder */
1151 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
1152 $queryBuilder->getRestrictions()
1153 ->removeAll()
1154 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1155 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1156
1157 $queryBuilder
1158 ->count('uid')
1159 ->from('tt_content')
1160 ->where(
1161 $queryBuilder->expr()->eq(
1162 'pid',
1163 $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1164 ),
1165 $queryBuilder->expr()->eq(
1166 'sys_language_uid',
1167 $queryBuilder->createNamedParameter($this->current_sys_language, \PDO::PARAM_INT)
1168 )
1169 );
1170
1171 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['disabled'])) {
1172 $andWhere[] = $queryBuilder->expr()->neq(
1173 'hidden',
1174 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1175 );
1176 }
1177
1178 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['starttime'])) {
1179 $andWhere[] = $queryBuilder->expr()->andX(
1180 $queryBuilder->expr()->neq(
1181 'starttime',
1182 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1183 ),
1184 $queryBuilder->expr()->gt(
1185 'starttime',
1186 $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1187 )
1188 );
1189 }
1190
1191 if (!empty($GLOBALS['TCA']['tt_content']['ctrl']['enablecolumns']['endtime'])) {
1192 $andWhere[] = $queryBuilder->expr()->andX(
1193 $queryBuilder->expr()->neq(
1194 'endtime',
1195 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1196 ),
1197 $queryBuilder->expr()->lte(
1198 'endtime',
1199 $queryBuilder->createNamedParameter($GLOBALS['SIM_ACCESS_TIME'], \PDO::PARAM_INT)
1200 )
1201 );
1202 }
1203
1204 if (!empty($andWhere)) {
1205 $queryBuilder->andWhere(
1206 $queryBuilder->expr()->orX(...$andWhere)
1207 );
1208 }
1209
1210 $count = $queryBuilder
1211 ->execute()
1212 ->fetchColumn(0);
1213
1214 return (int)$count;
1215 }
1216
1217 /**
1218 * Returns URL to the current script.
1219 * In particular the "popView" and "new_unique_uid" Get vars are unset.
1220 *
1221 * @param array $params Parameters array, merged with global GET vars.
1222 * @return string URL
1223 */
1224 public function local_linkThisScript($params)
1225 {
1226 $params['popView'] = '';
1227 $params['new_unique_uid'] = '';
1228 return GeneralUtility::linkThisScript($params);
1229 }
1230
1231 /**
1232 * Check if page can be edited by current user
1233 *
1234 * @return bool
1235 */
1236 public function pageIsNotLockedForEditors()
1237 {
1238 return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::PAGE_EDIT) === Permission::PAGE_EDIT && !$this->pageinfo['editlock'];
1239 }
1240
1241 /**
1242 * Check if content can be edited by current user
1243 *
1244 * @return bool
1245 */
1246 public function contentIsNotLockedForEditors()
1247 {
1248 return $this->getBackendUser()->isAdmin() || ($this->CALC_PERMS & Permission::CONTENT_EDIT) === Permission::CONTENT_EDIT && !$this->pageinfo['editlock'];
1249 }
1250
1251 /**
1252 * Returns LanguageService
1253 *
1254 * @return \TYPO3\CMS\Lang\LanguageService
1255 */
1256 protected function getLanguageService()
1257 {
1258 return $GLOBALS['LANG'];
1259 }
1260
1261 /**
1262 * Returns the current BE user.
1263 *
1264 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1265 */
1266 protected function getBackendUser()
1267 {
1268 return $GLOBALS['BE_USER'];
1269 }
1270
1271 /**
1272 * Returns current PageRenderer
1273 *
1274 * @return PageRenderer
1275 */
1276 protected function getPageRenderer()
1277 {
1278 return GeneralUtility::makeInstance(PageRenderer::class);
1279 }
1280
1281 /**
1282 * Make the LanguageMenu
1283 */
1284 protected function makeLanguageMenu()
1285 {
1286 if (count($this->MOD_MENU['language']) > 1) {
1287 $lang = $this->getLanguageService();
1288 $languageMenu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
1289 $languageMenu->setIdentifier('languageMenu');
1290 foreach ($this->MOD_MENU['language'] as $key => $language) {
1291 $menuItem = $languageMenu
1292 ->makeMenuItem()
1293 ->setTitle($language)
1294 ->setHref(BackendUtility::getModuleUrl($this->moduleName) . '&id=' . $this->id . '&SET[language]=' . $key);
1295 if ((int)$this->current_sys_language === $key) {
1296 $menuItem->setActive(true);
1297 }
1298 $languageMenu->addMenuItem($menuItem);
1299 }
1300 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($languageMenu);
1301 }
1302 }
1303
1304 /**
1305 * Checks whether the current page has sub pages
1306 *
1307 * @return bool
1308 */
1309 protected function currentPageHasSubPages()
1310 {
1311 /** @var QueryBuilder $queryBuilder */
1312 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1313 $queryBuilder->getRestrictions()
1314 ->removeAll()
1315 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1316 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
1317
1318 // get workspace id
1319 $workspaceId = (int)$this->getBackendUser()->workspace;
1320 $comparisonExpression = $workspaceId === 0 ? 'neq' : 'eq';
1321
1322 $count = $queryBuilder
1323 ->count('uid')
1324 ->from('pages')
1325 ->where(
1326 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)),
1327 $queryBuilder->expr()->eq(
1328 't3ver_wsid',
1329 $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
1330 ),
1331 $queryBuilder->expr()->{$comparisonExpression}(
1332 'pid',
1333 $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
1334 )
1335 )
1336 ->execute()
1337 ->fetchColumn(0);
1338
1339 return (bool)$count;
1340 }
1341 }