TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
PageRepository.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Frontend\Page;
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 
31 
41 {
45  public $urltypes = ['', 'http://', 'ftp://', 'mailto:', 'https://'];
46 
54  public $where_hid_del = ' AND pages.deleted=0';
55 
61  public $where_groupAccess = '';
62 
66  public $sys_language_uid = 0;
67 
75  public $versioningPreview = false;
76 
81 
88 
92  public $workspaceCache = [];
93 
99  public $error_getRootLine = '';
100 
107 
111  protected $cache_getRootLine = [];
112 
116  protected $cache_getPage = [];
117 
121  protected $cache_getPage_noCheck = [];
122 
127 
131  protected $cache_getMountPointInfo = [];
132 
137  'sys_file_metadata',
138  'sys_category',
139  ];
140 
147  '_LOCALIZED_UID',
148  '_MP_PARAM',
149  '_ORIG_uid',
150  '_ORIG_pid',
151  '_PAGES_OVERLAY',
152  '_PAGES_OVERLAY_UID',
153  '_PAGES_OVERLAY_LANGUAGE',
154  ];
155 
159  const DOKTYPE_DEFAULT = 1;
160  const DOKTYPE_LINK = 3;
161  const DOKTYPE_SHORTCUT = 4;
164  const DOKTYPE_SPACER = 199;
165  const DOKTYPE_SYSFOLDER = 254;
166  const DOKTYPE_RECYCLER = 255;
167 
175 
186  public function init($show_hidden)
187  {
188  $this->where_groupAccess = '';
189 
190  if ($this->versioningPreview) {
191  // For version previewing, make sure that enable-fields are not
192  // de-selecting hidden pages - we need versionOL() to unset them only
193  // if the overlay record instructs us to.
194  // Clear where_hid_del and restrict to live and current workspaces
195  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
196  ->getQueryBuilderForTable('pages')
197  ->expr();
198  $this->where_hid_del = ' AND ' . $expressionBuilder->andX(
199  $expressionBuilder->eq('pages.deleted', 0),
200  $expressionBuilder->orX(
201  $expressionBuilder->eq('pages.t3ver_wsid', 0),
202  $expressionBuilder->eq('pages.t3ver_wsid', (int)$this->versioningWorkspaceId)
203  )
204  );
205  } else {
206  // add starttime / endtime, and check for hidden/deleted
207  // Filter out new/deleted place-holder pages in case we are NOT in a
208  // versioning preview (that means we are online!)
209  $this->where_hid_del = $this->enableFields('pages', $show_hidden, ['fe_group' => true], true);
210  }
211  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'])) {
212  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'] as $classRef) {
213  $hookObject = GeneralUtility::makeInstance($classRef);
214  if (!$hookObject instanceof PageRepositoryInitHookInterface) {
215  throw new \UnexpectedValueException($hookObject . ' must implement interface ' . PageRepositoryInitHookInterface::class, 1379579812);
216  }
217  $hookObject->init_postProcess($this);
218  }
219  }
220  }
221 
222  /**************************
223  *
224  * Selecting page records
225  *
226  **************************/
227 
239  public function getPage($uid, $disableGroupAccessCheck = false)
240  {
241  // Hook to manipulate the page uid for special overlay handling
242  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'])) {
243  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'] as $classRef) {
244  $hookObject = GeneralUtility::getUserObj($classRef);
245  if (!$hookObject instanceof PageRepositoryGetPageHookInterface) {
246  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetPageHookInterface::class, 1251476766);
247  }
248  $hookObject->getPage_preProcess($uid, $disableGroupAccessCheck, $this);
249  }
250  }
251  $cacheKey = md5(
252  implode(
253  '-',
254  [
255  ($disableGroupAccessCheck ? '' : $this->where_groupAccess),
256  $this->where_hid_del,
257  $this->sys_language_uid
258  ]
259  )
260  );
261  if (is_array($this->cache_getPage[$uid][$cacheKey])) {
262  return $this->cache_getPage[$uid][$cacheKey];
263  }
264  $result = [];
265  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
266  $queryBuilder->getRestrictions()->removeAll();
267  $queryBuilder->select('*')
268  ->from('pages')
269  ->where(
270  $queryBuilder->expr()->eq('uid', (int)$uid),
271  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del)
272  );
273 
274  if (!$disableGroupAccessCheck) {
275  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess));
276  }
277 
278  $row = $queryBuilder->execute()->fetch();
279  if ($row) {
280  $this->versionOL('pages', $row);
281  if (is_array($row)) {
282  $result = $this->getPageOverlay($row);
283  }
284  }
285  $this->cache_getPage[$uid][$cacheKey] = $result;
286  return $result;
287  }
288 
297  public function getPage_noCheck($uid)
298  {
299  if ($this->cache_getPage_noCheck[$uid]) {
300  return $this->cache_getPage_noCheck[$uid];
301  }
302 
303  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
304  $queryBuilder->getRestrictions()
305  ->removeAll()
306  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
307  $row = $queryBuilder->select('*')
308  ->from('pages')
309  ->where($queryBuilder->expr()->eq('uid', (int)$uid))
310  ->execute()
311  ->fetch();
312 
313  $result = [];
314  if ($row) {
315  $this->versionOL('pages', $row);
316  if (is_array($row)) {
317  $result = $this->getPageOverlay($row);
318  }
319  }
320  $this->cache_getPage_noCheck[$uid] = $result;
321  return $result;
322  }
323 
331  public function getFirstWebPage($uid)
332  {
333  $output = '';
334  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
335  $queryBuilder->getRestrictions()->removeAll();
336  $row = $queryBuilder->select('*')
337  ->from('pages')
338  ->where(
339  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
340  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
341  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
342  )
343  ->orderBy('sorting')
344  ->setMaxResults(1)
345  ->execute()
346  ->fetch();
347 
348  if ($row) {
349  $this->versionOL('pages', $row);
350  if (is_array($row)) {
351  $output = $this->getPageOverlay($row);
352  }
353  }
354  return $output;
355  }
356 
364  public function getPageIdFromAlias($alias)
365  {
366  $alias = strtolower($alias);
367  if ($this->cache_getPageIdFromAlias[$alias]) {
368  return $this->cache_getPageIdFromAlias[$alias];
369  }
370  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
371  $queryBuilder->getRestrictions()
372  ->removeAll()
373  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
374 
375  $row = $queryBuilder->select('uid')
376  ->from('pages')
377  ->where(
378  $queryBuilder->expr()->eq('alias', $queryBuilder->createNamedParameter($alias, \PDO::PARAM_STR)),
379  // "AND pid>=0" because of versioning (means that aliases sent MUST be online!)
380  $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
381  )
382  ->setMaxResults(1)
383  ->execute()
384  ->fetch();
385 
386  if ($row) {
387  $this->cache_getPageIdFromAlias[$alias] = $row['uid'];
388  return $row['uid'];
389  }
390  $this->cache_getPageIdFromAlias[$alias] = 0;
391  return 0;
392  }
393 
402  public function getPageOverlay($pageInput, $lUid = -1)
403  {
404  $rows = $this->getPagesOverlay([$pageInput], $lUid);
405  // Always an array in return
406  return isset($rows[0]) ? $rows[0] : [];
407  }
408 
420  public function getPagesOverlay(array $pagesInput, $lUid = -1)
421  {
422  if (empty($pagesInput)) {
423  return [];
424  }
425  // Initialize:
426  if ($lUid < 0) {
427  $lUid = $this->sys_language_uid;
428  }
429  $row = null;
430  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'])) {
431  foreach ($pagesInput as &$origPage) {
432  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'] as $classRef) {
433  $hookObject = GeneralUtility::getUserObj($classRef);
434  if (!$hookObject instanceof PageRepositoryGetPageOverlayHookInterface) {
435  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetPageOverlayHookInterface::class, 1269878881);
436  }
437  $hookObject->getPageOverlay_preProcess($origPage, $lUid, $this);
438  }
439  }
440  unset($origPage);
441  }
442  // If language UID is different from zero, do overlay:
443  if ($lUid) {
444  $fieldArr = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields'], true);
445  $page_ids = [];
446 
447  $origPage = reset($pagesInput);
448  if (is_array($origPage)) {
449  // Make sure that only fields which exist in the first incoming record are overlaid!
450  $fieldArr = array_intersect($fieldArr, array_keys($this->purgeComputedProperties($origPage)));
451  }
452  foreach ($pagesInput as $origPage) {
453  if (is_array($origPage)) {
454  // Was the whole record
455  $page_ids[] = $origPage['uid'];
456  } else {
457  // Was the id
458  $page_ids[] = $origPage;
459  }
460  }
461  if (!empty($fieldArr)) {
462  if (!in_array('pid', $fieldArr, true)) {
463  $fieldArr[] = 'pid';
464  }
465  // NOTE regarding the query restrictions
466  // Currently the showHiddenRecords of TSFE set will allow
467  // pages_language_overlay records to be selected as they are
468  // child-records of a page.
469  // However you may argue that the showHiddenField flag should
470  // determine this. But that's not how it's done right now.
471  // Selecting overlay record:
472  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
473  ->getQueryBuilderForTable('pages_language_overlay');
474  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
475  $result = $queryBuilder->select(...$fieldArr)
476  ->from('pages_language_overlay')
477  ->where(
478  $queryBuilder->expr()->in(
479  'pid',
480  $queryBuilder->createNamedParameter($page_ids, Connection::PARAM_INT_ARRAY)
481  ),
482  $queryBuilder->expr()->eq(
483  'sys_language_uid',
484  $queryBuilder->createNamedParameter($lUid, \PDO::PARAM_INT)
485  )
486  )
487  ->execute();
488 
489  $overlays = [];
490  while ($row = $result->fetch()) {
491  $this->versionOL('pages_language_overlay', $row);
492  if (is_array($row)) {
493  $row['_PAGES_OVERLAY'] = true;
494  $row['_PAGES_OVERLAY_UID'] = $row['uid'];
495  $row['_PAGES_OVERLAY_LANGUAGE'] = $lUid;
496  $origUid = $row['pid'];
497  // Unset vital fields that are NOT allowed to be overlaid:
498  unset($row['uid']);
499  unset($row['pid']);
500  $overlays[$origUid] = $row;
501  }
502  }
503  }
504  }
505  // Create output:
506  $pagesOutput = [];
507  foreach ($pagesInput as $key => $origPage) {
508  if (is_array($origPage)) {
509  $pagesOutput[$key] = $origPage;
510  if (isset($overlays[$origPage['uid']])) {
511  // Overwrite the original field with the overlay
512  foreach ($overlays[$origPage['uid']] as $fieldName => $fieldValue) {
513  if ($fieldName !== 'uid' && $fieldName !== 'pid') {
514  if ($this->shouldFieldBeOverlaid('pages_language_overlay', $fieldName, $fieldValue)) {
515  $pagesOutput[$key][$fieldName] = $fieldValue;
516  }
517  }
518  }
519  }
520  } else {
521  if (isset($overlays[$origPage])) {
522  $pagesOutput[$key] = $overlays[$origPage];
523  }
524  }
525  }
526  return $pagesOutput;
527  }
528 
540  public function getRecordOverlay($table, $row, $sys_language_content, $OLmode = '')
541  {
542  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
543  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
544  $hookObject = GeneralUtility::getUserObj($classRef);
545  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
546  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881658);
547  }
548  $hookObject->getRecordOverlay_preProcess($table, $row, $sys_language_content, $OLmode, $this);
549  }
550  }
551  if ($row['uid'] > 0 && ($row['pid'] > 0 || in_array($table, $this->tableNamesAllowedOnRootLevel, true))) {
552  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
553  // Return record for ALL languages untouched
554  // TODO: Fix call stack to prevent this situation in the first place
555  if ($table !== 'pages_language_overlay' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] !== -1) {
556  // Will not be able to work with other tables (Just didn't implement it yet;
557  // Requires a scan over all tables [ctrl] part for first FIND the table that
558  // carries localization information for this table (which could even be more
559  // than a single table) and then use that. Could be implemented, but obviously
560  // takes a little more....) Will try to overlay a record only if the
561  // sys_language_content value is larger than zero.
562  if ($sys_language_content > 0) {
563  // Must be default language, otherwise no overlaying
564  if ((int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
565  // Select overlay record:
566  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
567  ->getQueryBuilderForTable($table);
568  $queryBuilder->setRestrictions(
569  GeneralUtility::makeInstance(FrontendRestrictionContainer::class)
570  );
571  $olrow = $queryBuilder->select('*')
572  ->from($table)
573  ->where(
574  $queryBuilder->expr()->eq(
575  'pid',
576  $queryBuilder->createNamedParameter($row['pid'], \PDO::PARAM_INT)
577  ),
578  $queryBuilder->expr()->eq(
579  $GLOBALS['TCA'][$table]['ctrl']['languageField'],
580  $queryBuilder->createNamedParameter($sys_language_content, \PDO::PARAM_INT)
581  ),
582  $queryBuilder->expr()->eq(
583  $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
584  $queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT)
585  )
586  )
587  ->setMaxResults(1)
588  ->execute()
589  ->fetch();
590 
591  $this->versionOL($table, $olrow);
592  // Merge record content by traversing all fields:
593  if (is_array($olrow)) {
594  if (isset($olrow['_ORIG_uid'])) {
595  $row['_ORIG_uid'] = $olrow['_ORIG_uid'];
596  }
597  if (isset($olrow['_ORIG_pid'])) {
598  $row['_ORIG_pid'] = $olrow['_ORIG_pid'];
599  }
600  foreach ($row as $fN => $fV) {
601  if ($fN !== 'uid' && $fN !== 'pid' && isset($olrow[$fN])) {
602  if ($this->shouldFieldBeOverlaid($table, $fN, $olrow[$fN])) {
603  $row[$fN] = $olrow[$fN];
604  }
605  } elseif ($fN === 'uid') {
606  $row['_LOCALIZED_UID'] = $olrow['uid'];
607  }
608  }
609  } elseif ($OLmode === 'hideNonTranslated' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
610  // Unset, if non-translated records should be hidden. ONLY done if the source
611  // record really is default language and not [All] in which case it is allowed.
612  unset($row);
613  }
614  } elseif ($sys_language_content != $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]) {
615  unset($row);
616  }
617  } else {
618  // When default language is displayed, we never want to return a record carrying
619  // another language!
620  if ($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
621  unset($row);
622  }
623  }
624  }
625  }
626  }
627  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
628  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
629  $hookObject = GeneralUtility::getUserObj($classRef);
630  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
631  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881659);
632  }
633  $hookObject->getRecordOverlay_postProcess($table, $row, $sys_language_content, $OLmode, $this);
634  }
635  }
636  return $row;
637  }
638 
639  /************************************************
640  *
641  * Page related: Menu, Domain record, Root line
642  *
643  ************************************************/
644 
661  public function getMenu($pageId, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
662  {
663  return $this->getSubpagesForPages((array)$pageId, $fields, $sortField, $additionalWhereClause, $checkShortcuts);
664  }
665 
679  public function getMenuForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
680  {
681  return $this->getSubpagesForPages($pageIds, $fields, $sortField, $additionalWhereClause, $checkShortcuts, false);
682  }
683 
703  protected function getSubpagesForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true, $parentPages = true)
704  {
705  $pages = [];
706  $relationField = $parentPages ? 'pid' : 'uid';
707  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
708  $queryBuilder->getRestrictions()->removeAll();
709 
710  $res = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
711  ->from('pages')
712  ->where(
713  $queryBuilder->expr()->in(
714  $relationField,
715  $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
716  ),
717  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
718  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
719  QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
720  );
721 
722  if (!empty($sortField)) {
723  $res->orderBy($sortField);
724  }
725  $result = $res->execute();
726 
727  while ($page = $result->fetch()) {
728  $originalUid = $page['uid'];
729 
730  // Versioning Preview Overlay
731  $this->versionOL('pages', $page, true);
732  // Skip if page got disabled due to version overlay
733  // (might be delete or move placeholder)
734  if (empty($page)) {
735  continue;
736  }
737 
738  // Add a mount point parameter if needed
739  $page = $this->addMountPointParameterToPage((array)$page);
740 
741  // If shortcut, look up if the target exists and is currently visible
742  if ($checkShortcuts) {
743  $page = $this->checkValidShortcutOfPage((array)$page, $additionalWhereClause);
744  }
745 
746  // If the page still is there, we add it to the output
747  if (!empty($page)) {
748  $pages[$originalUid] = $page;
749  }
750  }
751 
752  // Finally load language overlays
753  return $this->getPagesOverlay($pages);
754  }
755 
762  protected function addMountPointParameterToPage(array $page)
763  {
764  if (empty($page)) {
765  return [];
766  }
767 
768  // $page MUST have "uid", "pid", "doktype", "mount_pid", "mount_pid_ol" fields in it
769  $mountPointInfo = $this->getMountPointInfo($page['uid'], $page);
770 
771  // There is a valid mount point.
772  if (is_array($mountPointInfo) && $mountPointInfo['overlay']) {
773 
774  // Using "getPage" is OK since we need the check for enableFields AND for type 2
775  // of mount pids we DO require a doktype < 200!
776  $mountPointPage = $this->getPage($mountPointInfo['mount_pid']);
777 
778  if (!empty($mountPointPage)) {
779  $page = $mountPointPage;
780  $page['_MP_PARAM'] = $mountPointInfo['MPvar'];
781  } else {
782  $page = [];
783  }
784  }
785  return $page;
786  }
787 
795  protected function checkValidShortcutOfPage(array $page, $additionalWhereClause)
796  {
797  if (empty($page)) {
798  return [];
799  }
800 
801  $dokType = (int)$page['doktype'];
802  $shortcutMode = (int)$page['shortcut_mode'];
803 
804  if ($dokType === self::DOKTYPE_SHORTCUT && ($page['shortcut'] || $shortcutMode)) {
805  if ($shortcutMode === self::SHORTCUT_MODE_NONE) {
806  // No shortcut_mode set, so target is directly set in $page['shortcut']
807  $searchField = 'uid';
808  $searchUid = (int)$page['shortcut'];
809  } elseif ($shortcutMode === self::SHORTCUT_MODE_FIRST_SUBPAGE || $shortcutMode === self::SHORTCUT_MODE_RANDOM_SUBPAGE) {
810  // Check subpages - first subpage or random subpage
811  $searchField = 'pid';
812  // If a shortcut mode is set and no valid page is given to select subpags
813  // from use the actual page.
814  $searchUid = (int)$page['shortcut'] ?: $page['uid'];
815  } elseif ($shortcutMode === self::SHORTCUT_MODE_PARENT_PAGE) {
816  // Shortcut to parent page
817  $searchField = 'uid';
818  $searchUid = $page['pid'];
819  } else {
820  $searchField = '';
821  $searchUid = 0;
822  }
823 
824  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
825  $queryBuilder->getRestrictions()->removeAll();
826  $count = $queryBuilder->count('uid')
827  ->from('pages')
828  ->where(
829  $queryBuilder->expr()->eq(
830  $searchField,
831  $queryBuilder->createNamedParameter($searchUid, \PDO::PARAM_INT)
832  ),
833  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
834  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
835  QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
836  )
837  ->execute()
838  ->fetchColumn();
839 
840  if (!$count) {
841  $page = [];
842  }
843  } elseif ($dokType === self::DOKTYPE_SHORTCUT) {
844  // Neither shortcut target nor mode is set. Remove the page from the menu.
845  $page = [];
846  }
847  return $page;
848  }
860  public function getDomainStartPage($domain, $path = '', $request_uri = '')
861  {
862  $domain = explode(':', $domain);
863  $domain = strtolower(preg_replace('/\\.$/', '', $domain[0]));
864  // Removing extra trailing slashes
865  $path = trim(preg_replace('/\\/[^\\/]*$/', '', $path));
866  // Appending to domain string
867  $domain .= $path;
868  $domain = preg_replace('/\\/*$/', '', $domain);
869  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
870  $queryBuilder->getRestrictions()->removeAll();
871  $row = $queryBuilder
872  ->select(
873  'pages.uid',
874  'sys_domain.redirectTo',
875  'sys_domain.redirectHttpStatusCode',
876  'sys_domain.prepend_params'
877  )
878  ->from('pages')
879  ->from('sys_domain')
880  ->where(
881  $queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('sys_domain.pid')),
882  $queryBuilder->expr()->eq(
883  'sys_domain.hidden',
884  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
885  ),
886  $queryBuilder->expr()->orX(
887  $queryBuilder->expr()->eq(
888  'sys_domain.domainName',
889  $queryBuilder->createNamedParameter($domain, \PDO::PARAM_STR)
890  ),
891  $queryBuilder->expr()->eq(
892  'sys_domain.domainName',
893  $queryBuilder->createNamedParameter($domain . '/', \PDO::PARAM_STR)
894  )
895  ),
896  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
897  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
898  )
899  ->setMaxResults(1)
900  ->execute()
901  ->fetch();
902 
903  if (!$row) {
904  return '';
905  }
906 
907  if ($row['redirectTo']) {
908  $redirectUrl = $row['redirectTo'];
909  if ($row['prepend_params']) {
910  $redirectUrl = rtrim($redirectUrl, '/');
911  $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
912  $redirectUrl .= '/' . $prependStr;
913  }
914  $statusCode = (int)$row['redirectHttpStatusCode'];
915  if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
916  HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
917  } else {
919  }
920  die;
921  } else {
922  return $row['uid'];
923  }
924  }
925 
946  public function getRootLine($uid, $MP = '', $ignoreMPerrors = false)
947  {
948  $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $uid, $MP, $this);
949  try {
950  return $rootline->get();
951  } catch (\RuntimeException $ex) {
952  if ($ignoreMPerrors) {
953  $this->error_getRootLine = $ex->getMessage();
954  if (substr($this->error_getRootLine, -7) === 'uid -1.') {
955  $this->error_getRootLine_failPid = -1;
956  }
957  return [];
959  } elseif ($ex->getCode() === 1343589451) {
960  return [];
961  }
962  throw $ex;
963  }
964  }
965 
976  public function getPathFromRootline($rl, $len = 20)
977  {
979  $path = '';
980  if (is_array($rl)) {
981  $c = count($rl);
982  for ($a = 0; $a < $c; $a++) {
983  if ($rl[$a]['uid']) {
984  $path .= '/' . GeneralUtility::fixed_lgd_cs(strip_tags($rl[$a]['title']), $len);
985  }
986  }
987  }
988  return $path;
989  }
990 
998  public function getExtURL($pagerow)
999  {
1000  if ((int)$pagerow['doktype'] === self::DOKTYPE_LINK) {
1001  $redirectTo = $this->urltypes[$pagerow['urltype']] . $pagerow['url'];
1002  // If relative path, prefix Site URL:
1003  $uI = parse_url($redirectTo);
1004  // Relative path assumed now.
1005  if (!$uI['scheme'] && $redirectTo[0] !== '/') {
1006  $redirectTo = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $redirectTo;
1007  }
1008  return $redirectTo;
1009  }
1010  return false;
1011  }
1012 
1026  public function getMountPointInfo($pageId, $pageRec = false, $prevMountPids = [], $firstPageUid = 0)
1027  {
1028  $result = false;
1029  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
1030  if (isset($this->cache_getMountPointInfo[$pageId])) {
1031  return $this->cache_getMountPointInfo[$pageId];
1032  }
1033  // Get pageRec if not supplied:
1034  if (!is_array($pageRec)) {
1035  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1036  $queryBuilder->getRestrictions()
1037  ->removeAll()
1038  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1039 
1040  $pageRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
1041  ->from('pages')
1042  ->where(
1043  $queryBuilder->expr()->eq(
1044  'uid',
1045  $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
1046  ),
1047  $queryBuilder->expr()->neq(
1048  'doktype',
1049  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
1050  )
1051  )
1052  ->execute()
1053  ->fetch();
1054 
1055  // Only look for version overlay if page record is not supplied; This assumes
1056  // that the input record is overlaid with preview version, if any!
1057  $this->versionOL('pages', $pageRec);
1058  }
1059  // Set first Page uid:
1060  if (!$firstPageUid) {
1061  $firstPageUid = $pageRec['uid'];
1062  }
1063  // Look for mount pid value plus other required circumstances:
1064  $mount_pid = (int)$pageRec['mount_pid'];
1065  if (is_array($pageRec) && (int)$pageRec['doktype'] === self::DOKTYPE_MOUNTPOINT && $mount_pid > 0 && !in_array($mount_pid, $prevMountPids, true)) {
1066  // Get the mount point record (to verify its general existence):
1067  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1068  $queryBuilder->getRestrictions()
1069  ->removeAll()
1070  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1071 
1072  $mountRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
1073  ->from('pages')
1074  ->where(
1075  $queryBuilder->expr()->eq(
1076  'uid',
1077  $queryBuilder->createNamedParameter($mount_pid, \PDO::PARAM_INT)
1078  ),
1079  $queryBuilder->expr()->neq(
1080  'doktype',
1081  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
1082  )
1083  )
1084  ->execute()
1085  ->fetch();
1086 
1087  $this->versionOL('pages', $mountRec);
1088  if (is_array($mountRec)) {
1089  // Look for recursive mount point:
1090  $prevMountPids[] = $mount_pid;
1091  $recursiveMountPid = $this->getMountPointInfo($mount_pid, $mountRec, $prevMountPids, $firstPageUid);
1092  // Return mount point information:
1093  $result = $recursiveMountPid ?: [
1094  'mount_pid' => $mount_pid,
1095  'overlay' => $pageRec['mount_pid_ol'],
1096  'MPvar' => $mount_pid . '-' . $firstPageUid,
1097  'mount_point_rec' => $pageRec,
1098  'mount_pid_rec' => $mountRec
1099  ];
1100  } else {
1101  // Means, there SHOULD have been a mount point, but there was none!
1102  $result = -1;
1103  }
1104  }
1105  }
1106  $this->cache_getMountPointInfo[$pageId] = $result;
1107  return $result;
1108  }
1109 
1110  /********************************
1111  *
1112  * Selecting records in general
1113  *
1114  ********************************/
1115 
1125  public function checkRecord($table, $uid, $checkPage = 0)
1126  {
1127  $uid = (int)$uid;
1128  if (is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
1129  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1130  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1131  $row = $queryBuilder->select('*')
1132  ->from($table)
1133  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)))
1134  ->execute()
1135  ->fetch();
1136 
1137  if ($row) {
1138  $this->versionOL($table, $row);
1139  if (is_array($row)) {
1140  if ($checkPage) {
1141  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1142  ->getQueryBuilderForTable('pages');
1143  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1144  $numRows = (int)$queryBuilder->count('*')
1145  ->from('pages')
1146  ->where(
1147  $queryBuilder->expr()->eq(
1148  'uid',
1149  $queryBuilder->createNamedParameter($row['pid'], \PDO::PARAM_INT)
1150  )
1151  )
1152  ->execute()
1153  ->fetchColumn();
1154  if ($numRows > 0) {
1155  return $row;
1156  } else {
1157  return 0;
1158  }
1159  } else {
1160  return $row;
1161  }
1162  }
1163  }
1164  }
1165  return 0;
1166  }
1167 
1178  public function getRawRecord($table, $uid, $fields = '*', $noWSOL = false)
1179  {
1180  $uid = (int)$uid;
1181  if (isset($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
1182  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1183  $queryBuilder->getRestrictions()
1184  ->removeAll()
1185  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1186  $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1187  ->from($table)
1188  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)))
1189  ->execute()
1190  ->fetch();
1191 
1192  if ($row) {
1193  if (!$noWSOL) {
1194  $this->versionOL($table, $row);
1195  }
1196  if (is_array($row)) {
1197  return $row;
1198  }
1199  }
1200  }
1201  return 0;
1202  }
1203 
1216  public function getRecordsByField($theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
1217  {
1218  if (is_array($GLOBALS['TCA'][$theTable])) {
1219  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($theTable);
1220  $queryBuilder->getRestrictions()
1221  ->removeAll()
1222  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1223 
1224  $queryBuilder->select('*')
1225  ->from($theTable)
1226  ->where($queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($theValue)));
1227 
1228  if ($whereClause !== '') {
1229  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($whereClause));
1230  }
1231 
1232  if ($groupBy !== '') {
1233  $queryBuilder->groupBy(QueryHelper::parseGroupBy($groupBy));
1234  }
1235 
1236  if ($orderBy !== '') {
1237  foreach (QueryHelper::parseOrderBy($orderBy) as $orderPair) {
1238  list($fieldName, $order) = $orderPair;
1239  $queryBuilder->addOrderBy($fieldName, $order);
1240  }
1241  }
1242 
1243  if ($limit !== '') {
1244  if (strpos($limit, ',')) {
1245  $limitOffsetAndMax = GeneralUtility::intExplode(',', $limit);
1246  $queryBuilder->setFirstResult((int)$limitOffsetAndMax[0]);
1247  $queryBuilder->setMaxResults((int)$limitOffsetAndMax[1]);
1248  } else {
1249  $queryBuilder->setMaxResults((int)$limit);
1250  }
1251  }
1252 
1253  $rows = $queryBuilder->execute()->fetchAll();
1254 
1255  if (!empty($rows)) {
1256  return $rows;
1257  }
1258  }
1259  return null;
1260  }
1261 
1262  /********************************
1263  *
1264  * Caching and standard clauses
1265  *
1266  ********************************/
1267 
1279  public static function getHash($hash)
1280  {
1281  $hashContent = null;
1283  $contentHashCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
1284  $cacheEntry = $contentHashCache->get($hash);
1285  if ($cacheEntry) {
1286  $hashContent = $cacheEntry;
1287  }
1288  return $hashContent;
1289  }
1290 
1305  public static function storeHash($hash, $data, $ident, $lifetime = 0)
1306  {
1307  GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash')->set($hash, $data, ['ident_' . $ident], (int)$lifetime);
1308  }
1309 
1318  public function deleteClause($table)
1319  {
1320  return $GLOBALS['TCA'][$table]['ctrl']['delete'] ? ' AND ' . $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0' : '';
1321  }
1322 
1340  public function enableFields($table, $show_hidden = -1, $ignore_array = [], $noVersionPreview = false)
1341  {
1342  if ($show_hidden === -1 && is_object($this->getTypoScriptFrontendController())) {
1343  // If show_hidden was not set from outside and if TSFE is an object, set it
1344  // based on showHiddenPage and showHiddenRecords from TSFE
1345  $show_hidden = $table === 'pages' || $table === 'pages_language_overlay'
1346  ? $this->getTypoScriptFrontendController()->showHiddenPage
1347  : $this->getTypoScriptFrontendController()->showHiddenRecords;
1348  }
1349  if ($show_hidden === -1) {
1350  $show_hidden = 0;
1351  }
1352  // If show_hidden was not changed during the previous evaluation, do it here.
1353  $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
1354  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1355  ->getQueryBuilderForTable($table)
1356  ->expr();
1357  $constraints = [];
1358  if (is_array($ctrl)) {
1359  // Delete field check:
1360  if ($ctrl['delete']) {
1361  $constraints[] = $expressionBuilder->eq($table . '.' . $ctrl['delete'], 0);
1362  }
1363  if ($ctrl['versioningWS']) {
1364  if (!$this->versioningPreview) {
1365  // Filter out placeholder records (new/moved/deleted items)
1366  // in case we are NOT in a versioning preview (that means we are online!)
1367  $constraints[] = $expressionBuilder->lte(
1368  $table . '.t3ver_state',
1370  );
1371  } elseif ($table !== 'pages') {
1372  // show only records of live and of the current workspace
1373  // in case we are in a versioning preview
1374  $constraints[] = $expressionBuilder->orX(
1375  $expressionBuilder->eq($table . '.t3ver_wsid', 0),
1376  $expressionBuilder->eq($table . '.t3ver_wsid', (int)$this->versioningWorkspaceId)
1377  );
1378  }
1379 
1380  // Filter out versioned records
1381  if (!$noVersionPreview && empty($ignore_array['pid'])) {
1382  $constraints[] = $expressionBuilder->neq($table . '.pid', -1);
1383  }
1384  }
1385 
1386  // Enable fields:
1387  if (is_array($ctrl['enablecolumns'])) {
1388  // In case of versioning-preview, enableFields are ignored (checked in
1389  // versionOL())
1390  if (!$this->versioningPreview || !$ctrl['versioningWS'] || $noVersionPreview) {
1391  if ($ctrl['enablecolumns']['disabled'] && !$show_hidden && !$ignore_array['disabled']) {
1392  $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
1393  $constraints[] = $expressionBuilder->eq($field, 0);
1394  }
1395  if ($ctrl['enablecolumns']['starttime'] && !$ignore_array['starttime']) {
1396  $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
1397  $constraints[] = $expressionBuilder->lte($field, (int)$GLOBALS['SIM_ACCESS_TIME']);
1398  }
1399  if ($ctrl['enablecolumns']['endtime'] && !$ignore_array['endtime']) {
1400  $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
1401  $constraints[] = $expressionBuilder->orX(
1402  $expressionBuilder->eq($field, 0),
1403  $expressionBuilder->gt($field, (int)$GLOBALS['SIM_ACCESS_TIME'])
1404  );
1405  }
1406  if ($ctrl['enablecolumns']['fe_group'] && !$ignore_array['fe_group']) {
1407  $field = $table . '.' . $ctrl['enablecolumns']['fe_group'];
1408  $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
1409  $this->getMultipleGroupsWhereClause($field, $table)
1410  );
1411  }
1412  // Call hook functions for additional enableColumns
1413  // It is used by the extension ingmar_accessctrl which enables assigning more
1414  // than one usergroup to content and page records
1415  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) {
1416  $_params = [
1417  'table' => $table,
1418  'show_hidden' => $show_hidden,
1419  'ignore_array' => $ignore_array,
1420  'ctrl' => $ctrl
1421  ];
1422  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) {
1423  $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
1424  GeneralUtility::callUserFunction($_funcRef, $_params, $this)
1425  );
1426  }
1427  }
1428  }
1429  }
1430  } else {
1431  throw new \InvalidArgumentException('There is no entry in the $TCA array for the table "' . $table . '". This means that the function enableFields() is ' . 'called with an invalid table name as argument.', 1283790586);
1432  }
1433 
1434  return empty($constraints) ? '' : ' AND ' . $expressionBuilder->andX(...$constraints);
1435  }
1436 
1446  public function getMultipleGroupsWhereClause($field, $table)
1447  {
1448  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1449  ->getQueryBuilderForTable($table)
1450  ->expr();
1451  $memberGroups = GeneralUtility::intExplode(',', $this->getTypoScriptFrontendController()->gr_list);
1452  $orChecks = [];
1453  // If the field is empty, then OK
1454  $orChecks[] = $expressionBuilder->eq($field, $expressionBuilder->literal(''));
1455  // If the field is NULL, then OK
1456  $orChecks[] = $expressionBuilder->isNull($field);
1457  // If the field contains zero, then OK
1458  $orChecks[] = $expressionBuilder->eq($field, 0);
1459  foreach ($memberGroups as $value) {
1460  $orChecks[] = $expressionBuilder->inSet($field, $expressionBuilder->literal($value));
1461  }
1462 
1463  return' AND (' . $expressionBuilder->orX(...$orChecks) . ')';
1464  }
1465 
1466  /**********************
1467  *
1468  * Versioning Preview
1469  *
1470  **********************/
1471 
1491  public function fixVersioningPid($table, &$rr)
1492  {
1493  if ($this->versioningPreview && is_array($rr) && (int)$rr['pid'] === -1 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1494  $oid = 0;
1495  $wsid = 0;
1496  // Check values for t3ver_oid and t3ver_wsid:
1497  if (isset($rr['t3ver_oid']) && isset($rr['t3ver_wsid'])) {
1498  // If "t3ver_oid" is already a field, just set this:
1499  $oid = $rr['t3ver_oid'];
1500  $wsid = $rr['t3ver_wsid'];
1501  } else {
1502  // Otherwise we have to expect "uid" to be in the record and look up based
1503  // on this:
1504  $newPidRec = $this->getRawRecord($table, $rr['uid'], 't3ver_oid,t3ver_wsid', true);
1505  if (is_array($newPidRec)) {
1506  $oid = $newPidRec['t3ver_oid'];
1507  $wsid = $newPidRec['t3ver_wsid'];
1508  }
1509  }
1510  // If workspace ids matches and ID of current online version is found, look up
1511  // the PID value of that:
1512  if ($oid && ((int)$this->versioningWorkspaceId === 0 && $this->checkWorkspaceAccess($wsid) || (int)$wsid === (int)$this->versioningWorkspaceId)) {
1513  $oidRec = $this->getRawRecord($table, $oid, 'pid', true);
1514  if (is_array($oidRec)) {
1515  // SWAP uid as well? Well no, because when fixing a versioning PID happens it is
1516  // assumed that this is a "branch" type page and therefore the uid should be
1517  // kept (like in versionOL()). However if the page is NOT a branch version it
1518  // should not happen - but then again, direct access to that uid should not
1519  // happen!
1520  $rr['_ORIG_pid'] = $rr['pid'];
1521  $rr['pid'] = $oidRec['pid'];
1522  }
1523  }
1524  }
1525  // Changing PID in case of moving pointer:
1526  if ($movePlhRec = $this->getMovePlaceholder($table, $rr['uid'], 'pid')) {
1527  $rr['pid'] = $movePlhRec['pid'];
1528  }
1529  }
1530 
1551  public function versionOL($table, &$row, $unsetMovePointers = false, $bypassEnableFieldsCheck = false)
1552  {
1553  if ($this->versioningPreview && is_array($row)) {
1554  // will overlay any movePlhOL found with the real record, which in turn
1555  // will be overlaid with its workspace version if any.
1556  $movePldSwap = $this->movePlhOL($table, $row);
1557  // implode(',',array_keys($row)) = Using fields from original record to make
1558  // sure no additional fields are selected. This is best for eg. getPageOverlay()
1559  // Computed properties are excluded since those would lead to SQL errors.
1560  $fieldNames = implode(',', array_keys($this->purgeComputedProperties($row)));
1561  if ($wsAlt = $this->getWorkspaceVersionOfRecord($this->versioningWorkspaceId, $table, $row['uid'], $fieldNames, $bypassEnableFieldsCheck)) {
1562  if (is_array($wsAlt)) {
1563  // Always fix PID (like in fixVersioningPid() above). [This is usually not
1564  // the important factor for versioning OL]
1565  // Keep the old (-1) - indicates it was a version...
1566  $wsAlt['_ORIG_pid'] = $wsAlt['pid'];
1567  // Set in the online versions PID.
1568  $wsAlt['pid'] = $row['pid'];
1569  // For versions of single elements or page+content, preserve online UID and PID
1570  // (this will produce true "overlay" of element _content_, not any references)
1571  // For page+content the "_ORIG_uid" should actually be used as PID for selection.
1572  $wsAlt['_ORIG_uid'] = $wsAlt['uid'];
1573  $wsAlt['uid'] = $row['uid'];
1574  // Translate page alias as well so links are pointing to the _online_ page:
1575  if ($table === 'pages') {
1576  $wsAlt['alias'] = $row['alias'];
1577  }
1578  // Changing input record to the workspace version alternative:
1579  $row = $wsAlt;
1580  // Check if it is deleted/new
1581  $rowVersionState = VersionState::cast($row['t3ver_state']);
1582  if (
1583  $rowVersionState->equals(VersionState::NEW_PLACEHOLDER)
1584  || $rowVersionState->equals(VersionState::DELETE_PLACEHOLDER)
1585  ) {
1586  // Unset record if it turned out to be deleted in workspace
1587  $row = false;
1588  }
1589  // Check if move-pointer in workspace (unless if a move-placeholder is the
1590  // reason why it appears!):
1591  // You have to specifically set $unsetMovePointers in order to clear these
1592  // because it is normally a display issue if it should be shown or not.
1593  if (
1594  ($rowVersionState->equals(VersionState::MOVE_POINTER)
1595  && !$movePldSwap
1596  ) && $unsetMovePointers
1597  ) {
1598  // Unset record if it turned out to be deleted in workspace
1599  $row = false;
1600  }
1601  } else {
1602  // No version found, then check if t3ver_state = VersionState::NEW_PLACEHOLDER
1603  // (online version is dummy-representation)
1604  // Notice, that unless $bypassEnableFieldsCheck is TRUE, the $row is unset if
1605  // enablefields for BOTH the version AND the online record deselects it. See
1606  // note for $bypassEnableFieldsCheck
1608  $versionState = VersionState::cast($row['t3ver_state']);
1609  if ($wsAlt <= -1 || $versionState->indicatesPlaceholder()) {
1610  // Unset record if it turned out to be "hidden"
1611  $row = false;
1612  }
1613  }
1614  }
1615  }
1616  }
1617 
1628  public function movePlhOL($table, &$row)
1629  {
1630  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
1631  && (int)VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)
1632  ) {
1633  // Only for WS ver 2... (moving) - enabled by default with CMS7
1634  // If t3ver_move_id is not found, then find it (but we like best if it is here)
1635  if (!isset($row['t3ver_move_id'])) {
1636  $moveIDRec = $this->getRawRecord($table, $row['uid'], 't3ver_move_id', true);
1637  $moveID = $moveIDRec['t3ver_move_id'];
1638  } else {
1639  $moveID = $row['t3ver_move_id'];
1640  }
1641  // Find pointed-to record.
1642  if ($moveID) {
1643  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1644  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1645  $origRow = $queryBuilder->select(...array_keys($this->purgeComputedProperties($row)))
1646  ->from($table)
1647  ->where(
1648  $queryBuilder->expr()->eq(
1649  'uid',
1650  $queryBuilder->createNamedParameter($moveID, \PDO::PARAM_INT)
1651  )
1652  )
1653  ->setMaxResults(1)
1654  ->execute()
1655  ->fetch();
1656 
1657  if ($origRow) {
1658  $row = $origRow;
1659  return true;
1660  }
1661  }
1662  }
1663  return false;
1664  }
1665 
1675  public function getMovePlaceholder($table, $uid, $fields = '*')
1676  {
1677  if ($this->versioningPreview) {
1678  $workspace = (int)$this->versioningWorkspaceId;
1679  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $workspace !== 0) {
1680  // Select workspace version of record:
1681  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1682  $queryBuilder->getRestrictions()
1683  ->removeAll()
1684  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1685 
1686  $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1687  ->from($table)
1688  ->where(
1689  $queryBuilder->expr()->neq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1690  $queryBuilder->expr()->eq(
1691  't3ver_state',
1692  $queryBuilder->createNamedParameter(
1694  \PDO::PARAM_INT
1695  )
1696  ),
1697  $queryBuilder->expr()->eq(
1698  't3ver_move_id',
1699  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1700  ),
1701  $queryBuilder->expr()->eq(
1702  't3ver_wsid',
1703  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1704  )
1705  )
1706  ->setMaxResults(1)
1707  ->execute()
1708  ->fetch();
1709 
1710  if (is_array($row)) {
1711  return $row;
1712  }
1713  }
1714  }
1715  return false;
1716  }
1717 
1729  public function getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields = '*', $bypassEnableFieldsCheck = false)
1730  {
1731  if ($workspace !== 0 && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
1732  $workspace = (int)$workspace;
1733  $uid = (int)$uid;
1734  // Select workspace version of record, only testing for deleted.
1735  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1736  $queryBuilder->getRestrictions()
1737  ->removeAll()
1738  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1739 
1740  $newrow = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1741  ->from($table)
1742  ->where(
1743  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1744  $queryBuilder->expr()->eq(
1745  't3ver_oid',
1746  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1747  ),
1748  $queryBuilder->expr()->eq(
1749  't3ver_wsid',
1750  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1751  )
1752  )
1753  ->setMaxResults(1)
1754  ->execute()
1755  ->fetch();
1756 
1757  // If version found, check if it could have been selected with enableFields on
1758  // as well:
1759  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1760  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1761  // Remove the frontend workspace restriction because we are testing a version record
1762  $queryBuilder->getRestrictions()->removeByType(FrontendWorkspaceRestriction::class);
1763  $queryBuilder->select('uid')
1764  ->from($table)
1765  ->setMaxResults(1);
1766 
1767  if (is_array($newrow)) {
1768  $queryBuilder->where(
1769  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1770  $queryBuilder->expr()->eq(
1771  't3ver_oid',
1772  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1773  ),
1774  $queryBuilder->expr()->eq(
1775  't3ver_wsid',
1776  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1777  )
1778  );
1779  if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
1780  // Return offline version, tested for its enableFields.
1781  return $newrow;
1782  } else {
1783  // Return -1 because offline version was de-selected due to its enableFields.
1784  return -1;
1785  }
1786  } else {
1787  // OK, so no workspace version was found. Then check if online version can be
1788  // selected with full enable fields and if so, return 1:
1789  $queryBuilder->where(
1790  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
1791  );
1792  if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
1793  // Means search was done, but no version found.
1794  return 1;
1795  } else {
1796  // Return -2 because the online record was de-selected due to its enableFields.
1797  return -2;
1798  }
1799  }
1800  }
1801  // No look up in database because versioning not enabled / or workspace not
1802  // offline
1803  return false;
1804  }
1805 
1812  public function checkWorkspaceAccess($wsid)
1813  {
1814  if (!$this->getBackendUser() || !ExtensionManagementUtility::isLoaded('workspaces')) {
1815  return false;
1816  }
1817  if (isset($this->workspaceCache[$wsid])) {
1818  $ws = $this->workspaceCache[$wsid];
1819  } else {
1820  if ($wsid > 0) {
1821  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1822  ->getQueryBuilderForTable('sys_workspace');
1823  $queryBuilder->getRestrictions()->removeAll();
1824  $ws = $queryBuilder->select('*')
1825  ->from('sys_workspace')
1826  ->where(
1827  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($wsid, \PDO::PARAM_INT)),
1828  $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
1829  )
1830  ->execute()
1831  ->fetch();
1832  if (!is_array($ws)) {
1833  return false;
1834  }
1835  } else {
1836  $ws = $wsid;
1837  }
1838  $ws = $this->getBackendUser()->checkWorkspace($ws);
1839  $this->workspaceCache[$wsid] = $ws;
1840  }
1841  return (string)$ws['_ACCESS'] !== '';
1842  }
1843 
1852  public function getFileReferences($tableName, $fieldName, array $element)
1853  {
1855  $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
1856  $currentId = !empty($element['uid']) ? $element['uid'] : 0;
1857 
1858  // Fetch the references of the default element
1859  try {
1860  $references = $fileRepository->findByRelation($tableName, $fieldName, $currentId);
1861  } catch (FileDoesNotExistException $e) {
1866  return [];
1867  } catch (\InvalidArgumentException $e) {
1872  $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')';
1873  GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1874  return [];
1875  }
1876 
1877  $localizedId = null;
1878  if (isset($element['_LOCALIZED_UID'])) {
1879  $localizedId = $element['_LOCALIZED_UID'];
1880  } elseif (isset($element['_PAGES_OVERLAY_UID'])) {
1881  $localizedId = $element['_PAGES_OVERLAY_UID'];
1882  }
1883 
1884  if ($tableName === 'pages') {
1885  $tableName = 'pages_language_overlay';
1886  }
1887 
1888  $isTableLocalizable = (
1889  !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1890  && !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1891  );
1892  if ($isTableLocalizable && $localizedId !== null) {
1893  $localizedReferences = $fileRepository->findByRelation($tableName, $fieldName, $localizedId);
1894  $localizedReferencesValue = $localizedReferences ?: '';
1895  if ($this->shouldFieldBeOverlaid($tableName, $fieldName, $localizedReferencesValue)) {
1896  $references = $localizedReferences;
1897  }
1898  }
1899 
1900  return $references;
1901  }
1902 
1910  protected function purgeComputedProperties(array $row)
1911  {
1912  foreach ($this->computedPropertyNames as $computedPropertyName) {
1913  if (array_key_exists($computedPropertyName, $row)) {
1914  unset($row[$computedPropertyName]);
1915  }
1916  }
1917  return $row;
1918  }
1919 
1928  protected function shouldFieldBeOverlaid($table, $field, $value)
1929  {
1930  $l10n_mode = isset($GLOBALS['TCA'][$table]['columns'][$field]['l10n_mode'])
1931  ? $GLOBALS['TCA'][$table]['columns'][$field]['l10n_mode']
1932  : '';
1933 
1934  $shouldFieldBeOverlaid = true;
1935 
1936  if ($l10n_mode === 'exclude') {
1937  $shouldFieldBeOverlaid = false;
1938  } elseif ($l10n_mode === 'mergeIfNotBlank') {
1939  $checkValue = $value;
1940 
1941  // 0 values are considered blank when coming from a group field
1942  if (empty($value) && $GLOBALS['TCA'][$table]['columns'][$field]['config']['type'] === 'group') {
1943  $checkValue = '';
1944  }
1945 
1946  if ($checkValue === [] || !is_array($checkValue) && trim($checkValue) === '') {
1947  $shouldFieldBeOverlaid = false;
1948  }
1949  }
1950 
1951  return $shouldFieldBeOverlaid;
1952  }
1953 
1957  protected function getTypoScriptFrontendController()
1958  {
1959  return $GLOBALS['TSFE'];
1960  }
1961 
1967  protected function getBackendUser()
1968  {
1969  return $GLOBALS['BE_USER'];
1970  }
1971 }
getSubpagesForPages(array $pageIds, $fields= '*', $sortField= 'sorting', $additionalWhereClause= '', $checkShortcuts=true, $parentPages=true)
getMenuForPages(array $pageIds, $fields= '*', $sortField= 'sorting', $additionalWhereClause= '', $checkShortcuts=true)
shouldFieldBeOverlaid($table, $field, $value)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
getMountPointInfo($pageId, $pageRec=false, $prevMountPids=[], $firstPageUid=0)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
getRecordsByField($theTable, $theField, $theValue, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '')
getDomainStartPage($domain, $path= '', $request_uri= '')
static storeHash($hash, $data, $ident, $lifetime=0)
checkValidShortcutOfPage(array $page, $additionalWhereClause)
getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields= '*', $bypassEnableFieldsCheck=false)
getRootLine($uid, $MP= '', $ignoreMPerrors=false)
enableFields($table, $show_hidden=-1, $ignore_array=[], $noVersionPreview=false)
getRecordOverlay($table, $row, $sys_language_content, $OLmode= '')
checkRecord($table, $uid, $checkPage=0)
getMovePlaceholder($table, $uid, $fields= '*')
getPagesOverlay(array $pagesInput, $lUid=-1)
getPage($uid, $disableGroupAccessCheck=false)
getMenu($pageId, $fields= '*', $sortField= 'sorting', $additionalWhereClause= '', $checkShortcuts=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static stripLogicalOperatorPrefix(string $constraint)
getRawRecord($table, $uid, $fields= '*', $noWSOL=false)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)