TYPO3 CMS  TYPO3_8-7
PageRepository.php
Go to the documentation of this file.
1 <?php
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 
83 
87  public $workspaceCache = [];
88 
94  public $error_getRootLine = '';
95 
102 
106  protected $cache_getPage = [];
107 
111  protected $cache_getPage_noCheck = [];
112 
117 
121  protected $cache_getMountPointInfo = [];
122 
127  'sys_file_metadata',
128  'sys_category',
129  ];
130 
137  '_LOCALIZED_UID',
138  '_MP_PARAM',
139  '_ORIG_uid',
140  '_ORIG_pid',
141  '_PAGES_OVERLAY',
142  '_PAGES_OVERLAY_UID',
143  '_PAGES_OVERLAY_LANGUAGE',
144  ];
145 
149  const DOKTYPE_DEFAULT = 1;
150  const DOKTYPE_LINK = 3;
151  const DOKTYPE_SHORTCUT = 4;
154  const DOKTYPE_SPACER = 199;
155  const DOKTYPE_SYSFOLDER = 254;
156  const DOKTYPE_RECYCLER = 255;
157 
165 
175  public function init($show_hidden)
176  {
177  $this->where_groupAccess = '';
178 
179  if ($this->versioningPreview) {
180  // For version previewing, make sure that enable-fields are not
181  // de-selecting hidden pages - we need versionOL() to unset them only
182  // if the overlay record instructs us to.
183  // Clear where_hid_del and restrict to live and current workspaces
184  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
185  ->getQueryBuilderForTable('pages')
186  ->expr();
187  $this->where_hid_del = ' AND ' . $expressionBuilder->andX(
188  $expressionBuilder->eq('pages.deleted', 0),
189  $expressionBuilder->orX(
190  $expressionBuilder->eq('pages.t3ver_wsid', 0),
191  $expressionBuilder->eq('pages.t3ver_wsid', (int)$this->versioningWorkspaceId)
192  )
193  );
194  } else {
195  // add starttime / endtime, and check for hidden/deleted
196  // Filter out new/deleted place-holder pages in case we are NOT in a
197  // versioning preview (that means we are online!)
198  $this->where_hid_del = $this->enableFields('pages', $show_hidden, ['fe_group' => true], true);
199  }
200  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'])) {
201  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['init'] as $classRef) {
202  $hookObject = GeneralUtility::makeInstance($classRef);
203  if (!$hookObject instanceof PageRepositoryInitHookInterface) {
204  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryInitHookInterface::class, 1379579812);
205  }
206  $hookObject->init_postProcess($this);
207  }
208  }
209  }
210 
211  /**************************
212  *
213  * Selecting page records
214  *
215  **************************/
216 
246  public function getPage($uid, $disableGroupAccessCheck = false)
247  {
248  // Hook to manipulate the page uid for special overlay handling
249  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'])) {
250  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'] as $classRef) {
251  $hookObject = GeneralUtility::getUserObj($classRef);
252  if (!$hookObject instanceof PageRepositoryGetPageHookInterface) {
253  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetPageHookInterface::class, 1251476766);
254  }
255  $hookObject->getPage_preProcess($uid, $disableGroupAccessCheck, $this);
256  }
257  }
258  $cacheKey = md5(
259  implode(
260  '-',
261  [
262  ($disableGroupAccessCheck ? '' : $this->where_groupAccess),
263  $this->where_hid_del,
264  $this->sys_language_uid
265  ]
266  )
267  );
268  if (is_array($this->cache_getPage[$uid][$cacheKey])) {
269  return $this->cache_getPage[$uid][$cacheKey];
270  }
271  $result = [];
272  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
273  $queryBuilder->getRestrictions()->removeAll();
274  $queryBuilder->select('*')
275  ->from('pages')
276  ->where(
277  $queryBuilder->expr()->eq('uid', (int)$uid),
278  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del)
279  );
280 
281  if (!$disableGroupAccessCheck) {
282  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess));
283  }
284 
285  $row = $queryBuilder->execute()->fetch();
286  if ($row) {
287  $this->versionOL('pages', $row);
288  if (is_array($row)) {
289  $result = $this->getPageOverlay($row);
290  }
291  }
292  $this->cache_getPage[$uid][$cacheKey] = $result;
293  return $result;
294  }
295 
304  public function getPage_noCheck($uid)
305  {
306  if ($this->cache_getPage_noCheck[$uid]) {
307  return $this->cache_getPage_noCheck[$uid];
308  }
309 
310  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
311  $queryBuilder->getRestrictions()
312  ->removeAll()
313  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
314  $row = $queryBuilder->select('*')
315  ->from('pages')
316  ->where($queryBuilder->expr()->eq('uid', (int)$uid))
317  ->execute()
318  ->fetch();
319 
320  $result = [];
321  if ($row) {
322  $this->versionOL('pages', $row);
323  if (is_array($row)) {
324  $result = $this->getPageOverlay($row);
325  }
326  }
327  $this->cache_getPage_noCheck[$uid] = $result;
328  return $result;
329  }
330 
338  public function getFirstWebPage($uid)
339  {
340  $output = '';
341  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
342  $queryBuilder->getRestrictions()->removeAll();
343  $row = $queryBuilder->select('*')
344  ->from('pages')
345  ->where(
346  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)),
347  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
348  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
349  )
350  ->orderBy('sorting')
351  ->setMaxResults(1)
352  ->execute()
353  ->fetch();
354 
355  if ($row) {
356  $this->versionOL('pages', $row);
357  if (is_array($row)) {
358  $output = $this->getPageOverlay($row);
359  }
360  }
361  return $output;
362  }
363 
371  public function getPageIdFromAlias($alias)
372  {
373  $alias = strtolower($alias);
374  if ($this->cache_getPageIdFromAlias[$alias]) {
375  return $this->cache_getPageIdFromAlias[$alias];
376  }
377  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
378  $queryBuilder->getRestrictions()
379  ->removeAll()
380  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
381 
382  $row = $queryBuilder->select('uid')
383  ->from('pages')
384  ->where(
385  $queryBuilder->expr()->eq('alias', $queryBuilder->createNamedParameter($alias, \PDO::PARAM_STR)),
386  // "AND pid>=0" because of versioning (means that aliases sent MUST be online!)
387  $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
388  )
389  ->setMaxResults(1)
390  ->execute()
391  ->fetch();
392 
393  if ($row) {
394  $this->cache_getPageIdFromAlias[$alias] = $row['uid'];
395  return $row['uid'];
396  }
397  $this->cache_getPageIdFromAlias[$alias] = 0;
398  return 0;
399  }
400 
409  public function getPageOverlay($pageInput, $lUid = -1)
410  {
411  $rows = $this->getPagesOverlay([$pageInput], $lUid);
412  // Always an array in return
413  return isset($rows[0]) ? $rows[0] : [];
414  }
415 
427  public function getPagesOverlay(array $pagesInput, $lUid = -1)
428  {
429  if (empty($pagesInput)) {
430  return [];
431  }
432  // Initialize:
433  if ($lUid < 0) {
434  $lUid = $this->sys_language_uid;
435  }
436  $row = null;
437  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'])) {
438  foreach ($pagesInput as &$origPage) {
439  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'] as $classRef) {
440  $hookObject = GeneralUtility::getUserObj($classRef);
441  if (!$hookObject instanceof PageRepositoryGetPageOverlayHookInterface) {
442  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetPageOverlayHookInterface::class, 1269878881);
443  }
444  $hookObject->getPageOverlay_preProcess($origPage, $lUid, $this);
445  }
446  }
447  unset($origPage);
448  }
449  // If language UID is different from zero, do overlay:
450  if ($lUid) {
451  $page_ids = [];
452 
453  $origPage = reset($pagesInput);
454  foreach ($pagesInput as $origPage) {
455  if (is_array($origPage)) {
456  // Was the whole record
457  $page_ids[] = $origPage['uid'];
458  } else {
459  // Was the id
460  $page_ids[] = $origPage;
461  }
462  }
463  // NOTE regarding the query restrictions
464  // Currently the showHiddenRecords of TSFE set will allow
465  // pages_language_overlay records to be selected as they are
466  // child-records of a page.
467  // However you may argue that the showHiddenField flag should
468  // determine this. But that's not how it's done right now.
469  // Selecting overlay record:
470  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
471  ->getQueryBuilderForTable('pages_language_overlay');
472  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
473  $result = $queryBuilder->select('*')
474  ->from('pages_language_overlay')
475  ->where(
476  $queryBuilder->expr()->in(
477  'pid',
478  $queryBuilder->createNamedParameter($page_ids, Connection::PARAM_INT_ARRAY)
479  ),
480  $queryBuilder->expr()->eq(
481  'sys_language_uid',
482  $queryBuilder->createNamedParameter($lUid, \PDO::PARAM_INT)
483  )
484  )
485  ->execute();
486 
487  $overlays = [];
488  while ($row = $result->fetch()) {
489  $this->versionOL('pages_language_overlay', $row);
490  if (is_array($row)) {
491  $row['_PAGES_OVERLAY'] = true;
492  $row['_PAGES_OVERLAY_UID'] = $row['uid'];
493  $row['_PAGES_OVERLAY_LANGUAGE'] = $lUid;
494  $origUid = $row['pid'];
495  // Unset vital fields that are NOT allowed to be overlaid:
496  unset($row['uid']);
497  unset($row['pid']);
498  $overlays[$origUid] = $row;
499  }
500  }
501  }
502  // Create output:
503  $pagesOutput = [];
504  foreach ($pagesInput as $key => $origPage) {
505  if (is_array($origPage)) {
506  $pagesOutput[$key] = $origPage;
507  if (isset($overlays[$origPage['uid']])) {
508  // Overwrite the original field with the overlay
509  foreach ($overlays[$origPage['uid']] as $fieldName => $fieldValue) {
510  if ($fieldName !== 'uid' && $fieldName !== 'pid') {
511  $pagesOutput[$key][$fieldName] = $fieldValue;
512  }
513  }
514  }
515  } else {
516  if (isset($overlays[$origPage])) {
517  $pagesOutput[$key] = $overlays[$origPage];
518  }
519  }
520  }
521  return $pagesOutput;
522  }
523 
535  public function getRecordOverlay($table, $row, $sys_language_content, $OLmode = '')
536  {
537  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
538  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
539  $hookObject = GeneralUtility::getUserObj($classRef);
540  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
541  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881658);
542  }
543  $hookObject->getRecordOverlay_preProcess($table, $row, $sys_language_content, $OLmode, $this);
544  }
545  }
546  if ($row['uid'] > 0 && ($row['pid'] > 0 || in_array($table, $this->tableNamesAllowedOnRootLevel, true))) {
547  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
548  // Return record for ALL languages untouched
549  // TODO: Fix call stack to prevent this situation in the first place
550  if ($table !== 'pages_language_overlay' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] !== -1) {
551  // Will not be able to work with other tables (Just didn't implement it yet;
552  // Requires a scan over all tables [ctrl] part for first FIND the table that
553  // carries localization information for this table (which could even be more
554  // than a single table) and then use that. Could be implemented, but obviously
555  // takes a little more....) Will try to overlay a record only if the
556  // sys_language_content value is larger than zero.
557  if ($sys_language_content > 0) {
558  // Must be default language, otherwise no overlaying
559  if ((int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
560  // Select overlay record:
561  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
562  ->getQueryBuilderForTable($table);
563  $queryBuilder->setRestrictions(
564  GeneralUtility::makeInstance(FrontendRestrictionContainer::class)
565  );
566  $olrow = $queryBuilder->select('*')
567  ->from($table)
568  ->where(
569  $queryBuilder->expr()->eq(
570  'pid',
571  $queryBuilder->createNamedParameter($row['pid'], \PDO::PARAM_INT)
572  ),
573  $queryBuilder->expr()->eq(
574  $GLOBALS['TCA'][$table]['ctrl']['languageField'],
575  $queryBuilder->createNamedParameter($sys_language_content, \PDO::PARAM_INT)
576  ),
577  $queryBuilder->expr()->eq(
578  $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'],
579  $queryBuilder->createNamedParameter($row['uid'], \PDO::PARAM_INT)
580  )
581  )
582  ->setMaxResults(1)
583  ->execute()
584  ->fetch();
585 
586  $this->versionOL($table, $olrow);
587  // Merge record content by traversing all fields:
588  if (is_array($olrow)) {
589  if (isset($olrow['_ORIG_uid'])) {
590  $row['_ORIG_uid'] = $olrow['_ORIG_uid'];
591  }
592  if (isset($olrow['_ORIG_pid'])) {
593  $row['_ORIG_pid'] = $olrow['_ORIG_pid'];
594  }
595  foreach ($row as $fN => $fV) {
596  if ($fN !== 'uid' && $fN !== 'pid' && isset($olrow[$fN])) {
597  $row[$fN] = $olrow[$fN];
598  } elseif ($fN === 'uid') {
599  $row['_LOCALIZED_UID'] = $olrow['uid'];
600  }
601  }
602  } elseif ($OLmode === 'hideNonTranslated' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
603  // Unset, if non-translated records should be hidden. ONLY done if the source
604  // record really is default language and not [All] in which case it is allowed.
605  unset($row);
606  }
607  } elseif ($sys_language_content != $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]) {
608  unset($row);
609  }
610  } else {
611  // When default language is displayed, we never want to return a record carrying
612  // another language!
613  if ($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
614  unset($row);
615  }
616  }
617  }
618  }
619  }
620  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
621  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
622  $hookObject = GeneralUtility::getUserObj($classRef);
623  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
624  throw new \UnexpectedValueException($classRef . ' must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881659);
625  }
626  $hookObject->getRecordOverlay_postProcess($table, $row, $sys_language_content, $OLmode, $this);
627  }
628  }
629  return $row;
630  }
631 
632  /************************************************
633  *
634  * Page related: Menu, Domain record, Root line
635  *
636  ************************************************/
637 
654  public function getMenu($pageId, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
655  {
656  return $this->getSubpagesForPages((array)$pageId, $fields, $sortField, $additionalWhereClause, $checkShortcuts);
657  }
658 
672  public function getMenuForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
673  {
674  return $this->getSubpagesForPages($pageIds, $fields, $sortField, $additionalWhereClause, $checkShortcuts, false);
675  }
676 
696  protected function getSubpagesForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true, $parentPages = true)
697  {
698  $pages = [];
699  $relationField = $parentPages ? 'pid' : 'uid';
700  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
701  $queryBuilder->getRestrictions()->removeAll();
702 
703  $res = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
704  ->from('pages')
705  ->where(
706  $queryBuilder->expr()->in(
707  $relationField,
708  $queryBuilder->createNamedParameter($pageIds, Connection::PARAM_INT_ARRAY)
709  ),
710  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
711  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
712  QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
713  );
714 
715  if (!empty($sortField)) {
716  $orderBy = QueryHelper::parseOrderBy($sortField);
717  foreach ($orderBy as $order) {
718  $res->orderBy(...$order);
719  }
720  }
721  $result = $res->execute();
722 
723  while ($page = $result->fetch()) {
724  $originalUid = $page['uid'];
725 
726  // Versioning Preview Overlay
727  $this->versionOL('pages', $page, true);
728  // Skip if page got disabled due to version overlay
729  // (might be delete or move placeholder)
730  if (empty($page)) {
731  continue;
732  }
733 
734  // Add a mount point parameter if needed
735  $page = $this->addMountPointParameterToPage((array)$page);
736 
737  // If shortcut, look up if the target exists and is currently visible
738  if ($checkShortcuts) {
739  $page = $this->checkValidShortcutOfPage((array)$page, $additionalWhereClause);
740  }
741 
742  // If the page still is there, we add it to the output
743  if (!empty($page)) {
744  $pages[$originalUid] = $page;
745  }
746  }
747 
748  // Finally load language overlays
749  return $this->getPagesOverlay($pages);
750  }
751 
767  protected function addMountPointParameterToPage(array $page): array
768  {
769  if (empty($page)) {
770  return [];
771  }
772 
773  // $page MUST have "uid", "pid", "doktype", "mount_pid", "mount_pid_ol" fields in it
774  $mountPointInfo = $this->getMountPointInfo($page['uid'], $page);
775 
776  // There is a valid mount point in overlay mode.
777  if (is_array($mountPointInfo) && $mountPointInfo['overlay']) {
778 
779  // Using "getPage" is OK since we need the check for enableFields AND for type 2
780  // of mount pids we DO require a doktype < 200!
781  $mountPointPage = $this->getPage($mountPointInfo['mount_pid']);
782 
783  if (!empty($mountPointPage)) {
784  $page = $mountPointPage;
785  $page['_MP_PARAM'] = $mountPointInfo['MPvar'];
786  } else {
787  $page = [];
788  }
789  }
790  return $page;
791  }
792 
800  protected function checkValidShortcutOfPage(array $page, $additionalWhereClause)
801  {
802  if (empty($page)) {
803  return [];
804  }
805 
806  $dokType = (int)$page['doktype'];
807  $shortcutMode = (int)$page['shortcut_mode'];
808 
809  if ($dokType === self::DOKTYPE_SHORTCUT && ($page['shortcut'] || $shortcutMode)) {
810  if ($shortcutMode === self::SHORTCUT_MODE_NONE) {
811  // No shortcut_mode set, so target is directly set in $page['shortcut']
812  $searchField = 'uid';
813  $searchUid = (int)$page['shortcut'];
814  } elseif ($shortcutMode === self::SHORTCUT_MODE_FIRST_SUBPAGE || $shortcutMode === self::SHORTCUT_MODE_RANDOM_SUBPAGE) {
815  // Check subpages - first subpage or random subpage
816  $searchField = 'pid';
817  // If a shortcut mode is set and no valid page is given to select subpags
818  // from use the actual page.
819  $searchUid = (int)$page['shortcut'] ?: $page['uid'];
820  } elseif ($shortcutMode === self::SHORTCUT_MODE_PARENT_PAGE) {
821  // Shortcut to parent page
822  $searchField = 'uid';
823  $searchUid = $page['pid'];
824  } else {
825  $searchField = '';
826  $searchUid = 0;
827  }
828 
829  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
830  $queryBuilder->getRestrictions()->removeAll();
831  $count = $queryBuilder->count('uid')
832  ->from('pages')
833  ->where(
834  $queryBuilder->expr()->eq(
835  $searchField,
836  $queryBuilder->createNamedParameter($searchUid, \PDO::PARAM_INT)
837  ),
838  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
839  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess),
840  QueryHelper::stripLogicalOperatorPrefix($additionalWhereClause)
841  )
842  ->execute()
843  ->fetchColumn();
844 
845  if (!$count) {
846  $page = [];
847  }
848  } elseif ($dokType === self::DOKTYPE_SHORTCUT) {
849  // Neither shortcut target nor mode is set. Remove the page from the menu.
850  $page = [];
851  }
852  return $page;
853  }
865  public function getDomainStartPage($domain, $path = '', $request_uri = '')
866  {
867  $domain = explode(':', $domain);
868  $domain = strtolower(preg_replace('/\\.$/', '', $domain[0]));
869  // Removing extra trailing slashes
870  $path = trim(preg_replace('/\\/[^\\/]*$/', '', $path));
871  // Appending to domain string
872  $domain .= $path;
873  $domain = preg_replace('/\\/*$/', '', $domain);
874  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
875  $queryBuilder->getRestrictions()->removeAll();
876  $row = $queryBuilder
877  ->select(
878  'pages.uid',
879  'sys_domain.redirectTo',
880  'sys_domain.redirectHttpStatusCode',
881  'sys_domain.prepend_params'
882  )
883  ->from('pages')
884  ->from('sys_domain')
885  ->where(
886  $queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('sys_domain.pid')),
887  $queryBuilder->expr()->eq(
888  'sys_domain.hidden',
889  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
890  ),
891  $queryBuilder->expr()->orX(
892  $queryBuilder->expr()->eq(
893  'sys_domain.domainName',
894  $queryBuilder->createNamedParameter($domain, \PDO::PARAM_STR)
895  ),
896  $queryBuilder->expr()->eq(
897  'sys_domain.domainName',
898  $queryBuilder->createNamedParameter($domain . '/', \PDO::PARAM_STR)
899  )
900  ),
901  QueryHelper::stripLogicalOperatorPrefix($this->where_hid_del),
902  QueryHelper::stripLogicalOperatorPrefix($this->where_groupAccess)
903  )
904  ->setMaxResults(1)
905  ->execute()
906  ->fetch();
907 
908  if (!$row) {
909  return '';
910  }
911 
912  if ($row['redirectTo']) {
913  $redirectUrl = $row['redirectTo'];
914  if ($row['prepend_params']) {
915  $redirectUrl = rtrim($redirectUrl, '/');
916  $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
917  $redirectUrl .= '/' . $prependStr;
918  }
919  $statusCode = (int)$row['redirectHttpStatusCode'];
920  if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
921  HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
922  } else {
924  }
925  die;
926  }
927  return $row['uid'];
928  }
929 
950  public function getRootLine($uid, $MP = '', $ignoreMPerrors = false)
951  {
952  $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $uid, $MP, $this);
953  try {
954  return $rootline->get();
955  } catch (\RuntimeException $ex) {
956  if ($ignoreMPerrors) {
957  $this->error_getRootLine = $ex->getMessage();
958  if (substr($this->error_getRootLine, -7) === 'uid -1.') {
959  $this->error_getRootLine_failPid = -1;
960  }
961  return [];
962  }
963  if ($ex->getCode() === 1343589451) {
965  return [];
966  }
967  throw $ex;
968  }
969  }
970 
981  public function getPathFromRootline($rl, $len = 20)
982  {
984  $path = '';
985  if (is_array($rl)) {
986  $c = count($rl);
987  for ($a = 0; $a < $c; $a++) {
988  if ($rl[$a]['uid']) {
989  $path .= '/' . GeneralUtility::fixed_lgd_cs(strip_tags($rl[$a]['title']), $len);
990  }
991  }
992  }
993  return $path;
994  }
995 
1003  public function getExtURL($pagerow)
1004  {
1005  if ((int)$pagerow['doktype'] === self::DOKTYPE_LINK) {
1006  $redirectTo = $this->urltypes[$pagerow['urltype']] . $pagerow['url'];
1007  // If relative path, prefix Site URL:
1008  $uI = parse_url($redirectTo);
1009  // Relative path assumed now.
1010  if (!$uI['scheme'] && $redirectTo[0] !== '/') {
1011  $redirectTo = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $redirectTo;
1012  }
1013  return $redirectTo;
1014  }
1015  return false;
1016  }
1017 
1050  public function getMountPointInfo($pageId, $pageRec = false, $prevMountPids = [], $firstPageUid = 0)
1051  {
1052  $result = false;
1053  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
1054  if (isset($this->cache_getMountPointInfo[$pageId])) {
1055  return $this->cache_getMountPointInfo[$pageId];
1056  }
1057  // Get pageRec if not supplied:
1058  if (!is_array($pageRec)) {
1059  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1060  $queryBuilder->getRestrictions()
1061  ->removeAll()
1062  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1063 
1064  $pageRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
1065  ->from('pages')
1066  ->where(
1067  $queryBuilder->expr()->eq(
1068  'uid',
1069  $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
1070  ),
1071  $queryBuilder->expr()->neq(
1072  'doktype',
1073  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
1074  )
1075  )
1076  ->execute()
1077  ->fetch();
1078 
1079  // Only look for version overlay if page record is not supplied; This assumes
1080  // that the input record is overlaid with preview version, if any!
1081  $this->versionOL('pages', $pageRec);
1082  }
1083  // Set first Page uid:
1084  if (!$firstPageUid) {
1085  $firstPageUid = $pageRec['uid'];
1086  }
1087  // Look for mount pid value plus other required circumstances:
1088  $mount_pid = (int)$pageRec['mount_pid'];
1089  if (is_array($pageRec) && (int)$pageRec['doktype'] === self::DOKTYPE_MOUNTPOINT && $mount_pid > 0 && !in_array($mount_pid, $prevMountPids, true)) {
1090  // Get the mount point record (to verify its general existence):
1091  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1092  $queryBuilder->getRestrictions()
1093  ->removeAll()
1094  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1095 
1096  $mountRec = $queryBuilder->select('uid', 'pid', 'doktype', 'mount_pid', 'mount_pid_ol', 't3ver_state')
1097  ->from('pages')
1098  ->where(
1099  $queryBuilder->expr()->eq(
1100  'uid',
1101  $queryBuilder->createNamedParameter($mount_pid, \PDO::PARAM_INT)
1102  ),
1103  $queryBuilder->expr()->neq(
1104  'doktype',
1105  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
1106  )
1107  )
1108  ->execute()
1109  ->fetch();
1110 
1111  $this->versionOL('pages', $mountRec);
1112  if (is_array($mountRec)) {
1113  // Look for recursive mount point:
1114  $prevMountPids[] = $mount_pid;
1115  $recursiveMountPid = $this->getMountPointInfo($mount_pid, $mountRec, $prevMountPids, $firstPageUid);
1116  // Return mount point information:
1117  $result = $recursiveMountPid ?: [
1118  'mount_pid' => $mount_pid,
1119  'overlay' => $pageRec['mount_pid_ol'],
1120  'MPvar' => $mount_pid . '-' . $firstPageUid,
1121  'mount_point_rec' => $pageRec,
1122  'mount_pid_rec' => $mountRec
1123  ];
1124  } else {
1125  // Means, there SHOULD have been a mount point, but there was none!
1126  $result = -1;
1127  }
1128  }
1129  }
1130  $this->cache_getMountPointInfo[$pageId] = $result;
1131  return $result;
1132  }
1133 
1134  /********************************
1135  *
1136  * Selecting records in general
1137  *
1138  ********************************/
1139 
1149  public function checkRecord($table, $uid, $checkPage = 0)
1150  {
1151  $uid = (int)$uid;
1152  if (is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
1153  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1154  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1155  $row = $queryBuilder->select('*')
1156  ->from($table)
1157  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)))
1158  ->execute()
1159  ->fetch();
1160 
1161  if ($row) {
1162  $this->versionOL($table, $row);
1163  if (is_array($row)) {
1164  if ($checkPage) {
1165  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1166  ->getQueryBuilderForTable('pages');
1167  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1168  $numRows = (int)$queryBuilder->count('*')
1169  ->from('pages')
1170  ->where(
1171  $queryBuilder->expr()->eq(
1172  'uid',
1173  $queryBuilder->createNamedParameter($row['pid'], \PDO::PARAM_INT)
1174  )
1175  )
1176  ->execute()
1177  ->fetchColumn();
1178  if ($numRows > 0) {
1179  return $row;
1180  }
1181  return 0;
1182  }
1183  return $row;
1184  }
1185  }
1186  }
1187  return 0;
1188  }
1189 
1200  public function getRawRecord($table, $uid, $fields = '*', $noWSOL = false)
1201  {
1202  $uid = (int)$uid;
1203  if (isset($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
1204  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1205  $queryBuilder->getRestrictions()
1206  ->removeAll()
1207  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1208  $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1209  ->from($table)
1210  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)))
1211  ->execute()
1212  ->fetch();
1213 
1214  if ($row) {
1215  if (!$noWSOL) {
1216  $this->versionOL($table, $row);
1217  }
1218  if (is_array($row)) {
1219  return $row;
1220  }
1221  }
1222  }
1223  return 0;
1224  }
1225 
1238  public function getRecordsByField($theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
1239  {
1240  if (is_array($GLOBALS['TCA'][$theTable])) {
1241  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($theTable);
1242  $queryBuilder->getRestrictions()
1243  ->removeAll()
1244  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1245 
1246  $queryBuilder->select('*')
1247  ->from($theTable)
1248  ->where($queryBuilder->expr()->eq($theField, $queryBuilder->createNamedParameter($theValue)));
1249 
1250  if ($whereClause !== '') {
1251  $queryBuilder->andWhere(QueryHelper::stripLogicalOperatorPrefix($whereClause));
1252  }
1253 
1254  if ($groupBy !== '') {
1255  $queryBuilder->groupBy(QueryHelper::parseGroupBy($groupBy));
1256  }
1257 
1258  if ($orderBy !== '') {
1259  foreach (QueryHelper::parseOrderBy($orderBy) as $orderPair) {
1260  list($fieldName, $order) = $orderPair;
1261  $queryBuilder->addOrderBy($fieldName, $order);
1262  }
1263  }
1264 
1265  if ($limit !== '') {
1266  if (strpos($limit, ',')) {
1267  $limitOffsetAndMax = GeneralUtility::intExplode(',', $limit);
1268  $queryBuilder->setFirstResult((int)$limitOffsetAndMax[0]);
1269  $queryBuilder->setMaxResults((int)$limitOffsetAndMax[1]);
1270  } else {
1271  $queryBuilder->setMaxResults((int)$limit);
1272  }
1273  }
1274 
1275  $rows = $queryBuilder->execute()->fetchAll();
1276 
1277  if (!empty($rows)) {
1278  return $rows;
1279  }
1280  }
1281  return null;
1282  }
1283 
1284  /********************************
1285  *
1286  * Caching and standard clauses
1287  *
1288  ********************************/
1289 
1302  public static function getHash($hash)
1303  {
1305  $hashContent = null;
1307  $contentHashCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
1308  $cacheEntry = $contentHashCache->get($hash);
1309  if ($cacheEntry) {
1310  $hashContent = $cacheEntry;
1311  }
1312  return $hashContent;
1313  }
1314 
1329  public static function storeHash($hash, $data, $ident, $lifetime = 0)
1330  {
1332  GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash')->set($hash, $data, ['ident_' . $ident], (int)$lifetime);
1333  }
1334 
1343  public function deleteClause($table)
1344  {
1345  return $GLOBALS['TCA'][$table]['ctrl']['delete'] ? ' AND ' . $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0' : '';
1346  }
1347 
1365  public function enableFields($table, $show_hidden = -1, $ignore_array = [], $noVersionPreview = false)
1366  {
1367  if ($show_hidden === -1 && is_object($this->getTypoScriptFrontendController())) {
1368  // If show_hidden was not set from outside and if TSFE is an object, set it
1369  // based on showHiddenPage and showHiddenRecords from TSFE
1370  $show_hidden = $table === 'pages' || $table === 'pages_language_overlay'
1371  ? $this->getTypoScriptFrontendController()->showHiddenPage
1372  : $this->getTypoScriptFrontendController()->showHiddenRecords;
1373  }
1374  if ($show_hidden === -1) {
1375  $show_hidden = 0;
1376  }
1377  // If show_hidden was not changed during the previous evaluation, do it here.
1378  $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
1379  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1380  ->getQueryBuilderForTable($table)
1381  ->expr();
1382  $constraints = [];
1383  if (is_array($ctrl)) {
1384  // Delete field check:
1385  if ($ctrl['delete']) {
1386  $constraints[] = $expressionBuilder->eq($table . '.' . $ctrl['delete'], 0);
1387  }
1388  if ($ctrl['versioningWS']) {
1389  if (!$this->versioningPreview) {
1390  // Filter out placeholder records (new/moved/deleted items)
1391  // in case we are NOT in a versioning preview (that means we are online!)
1392  $constraints[] = $expressionBuilder->lte(
1393  $table . '.t3ver_state',
1395  );
1396  } elseif ($table !== 'pages') {
1397  // show only records of live and of the current workspace
1398  // in case we are in a versioning preview
1399  $constraints[] = $expressionBuilder->orX(
1400  $expressionBuilder->eq($table . '.t3ver_wsid', 0),
1401  $expressionBuilder->eq($table . '.t3ver_wsid', (int)$this->versioningWorkspaceId)
1402  );
1403  }
1404 
1405  // Filter out versioned records
1406  if (!$noVersionPreview && empty($ignore_array['pid'])) {
1407  $constraints[] = $expressionBuilder->neq($table . '.pid', -1);
1408  }
1409  }
1410 
1411  // Enable fields:
1412  if (is_array($ctrl['enablecolumns'])) {
1413  // In case of versioning-preview, enableFields are ignored (checked in
1414  // versionOL())
1415  if (!$this->versioningPreview || !$ctrl['versioningWS'] || $noVersionPreview) {
1416  if ($ctrl['enablecolumns']['disabled'] && !$show_hidden && !$ignore_array['disabled']) {
1417  $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
1418  $constraints[] = $expressionBuilder->eq($field, 0);
1419  }
1420  if ($ctrl['enablecolumns']['starttime'] && !$ignore_array['starttime']) {
1421  $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
1422  $constraints[] = $expressionBuilder->lte($field, (int)$GLOBALS['SIM_ACCESS_TIME']);
1423  }
1424  if ($ctrl['enablecolumns']['endtime'] && !$ignore_array['endtime']) {
1425  $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
1426  $constraints[] = $expressionBuilder->orX(
1427  $expressionBuilder->eq($field, 0),
1428  $expressionBuilder->gt($field, (int)$GLOBALS['SIM_ACCESS_TIME'])
1429  );
1430  }
1431  if ($ctrl['enablecolumns']['fe_group'] && !$ignore_array['fe_group']) {
1432  $field = $table . '.' . $ctrl['enablecolumns']['fe_group'];
1433  $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
1434  $this->getMultipleGroupsWhereClause($field, $table)
1435  );
1436  }
1437  // Call hook functions for additional enableColumns
1438  // It is used by the extension ingmar_accessctrl which enables assigning more
1439  // than one usergroup to content and page records
1440  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) {
1441  $_params = [
1442  'table' => $table,
1443  'show_hidden' => $show_hidden,
1444  'ignore_array' => $ignore_array,
1445  'ctrl' => $ctrl
1446  ];
1447  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) {
1448  $constraints[] = QueryHelper::stripLogicalOperatorPrefix(
1449  GeneralUtility::callUserFunction($_funcRef, $_params, $this)
1450  );
1451  }
1452  }
1453  }
1454  }
1455  } else {
1456  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);
1457  }
1458 
1459  return empty($constraints) ? '' : ' AND ' . $expressionBuilder->andX(...$constraints);
1460  }
1461 
1471  public function getMultipleGroupsWhereClause($field, $table)
1472  {
1473  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1474  ->getQueryBuilderForTable($table)
1475  ->expr();
1476  $memberGroups = GeneralUtility::intExplode(',', $this->getTypoScriptFrontendController()->gr_list);
1477  $orChecks = [];
1478  // If the field is empty, then OK
1479  $orChecks[] = $expressionBuilder->eq($field, $expressionBuilder->literal(''));
1480  // If the field is NULL, then OK
1481  $orChecks[] = $expressionBuilder->isNull($field);
1482  // If the field contains zero, then OK
1483  $orChecks[] = $expressionBuilder->eq($field, $expressionBuilder->literal('0'));
1484  foreach ($memberGroups as $value) {
1485  $orChecks[] = $expressionBuilder->inSet($field, $expressionBuilder->literal($value));
1486  }
1487 
1488  return' AND (' . $expressionBuilder->orX(...$orChecks) . ')';
1489  }
1490 
1491  /**********************
1492  *
1493  * Versioning Preview
1494  *
1495  **********************/
1496 
1515  public function fixVersioningPid($table, &$rr)
1516  {
1517  if ($this->versioningPreview && is_array($rr) && (int)$rr['pid'] === -1 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1518  $oid = 0;
1519  $wsid = 0;
1520  // Check values for t3ver_oid and t3ver_wsid:
1521  if (isset($rr['t3ver_oid']) && isset($rr['t3ver_wsid'])) {
1522  // If "t3ver_oid" is already a field, just set this:
1523  $oid = $rr['t3ver_oid'];
1524  $wsid = $rr['t3ver_wsid'];
1525  } else {
1526  // Otherwise we have to expect "uid" to be in the record and look up based
1527  // on this:
1528  $newPidRec = $this->getRawRecord($table, $rr['uid'], 't3ver_oid,t3ver_wsid', true);
1529  if (is_array($newPidRec)) {
1530  $oid = $newPidRec['t3ver_oid'];
1531  $wsid = $newPidRec['t3ver_wsid'];
1532  }
1533  }
1534  // If workspace ids matches and ID of current online version is found, look up
1535  // the PID value of that:
1536  if ($oid && ((int)$this->versioningWorkspaceId === 0 && $this->checkWorkspaceAccess($wsid) || (int)$wsid === (int)$this->versioningWorkspaceId)) {
1537  $oidRec = $this->getRawRecord($table, $oid, 'pid', true);
1538  if (is_array($oidRec)) {
1539  // SWAP uid as well? Well no, because when fixing a versioning PID happens it is
1540  // assumed that this is a "branch" type page and therefore the uid should be
1541  // kept (like in versionOL()). However if the page is NOT a branch version it
1542  // should not happen - but then again, direct access to that uid should not
1543  // happen!
1544  $rr['_ORIG_pid'] = $rr['pid'];
1545  $rr['pid'] = $oidRec['pid'];
1546  }
1547  }
1548  }
1549  // Changing PID in case of moving pointer:
1550  if ($movePlhRec = $this->getMovePlaceholder($table, $rr['uid'], 'pid')) {
1551  $rr['pid'] = $movePlhRec['pid'];
1552  }
1553  }
1554 
1574  public function versionOL($table, &$row, $unsetMovePointers = false, $bypassEnableFieldsCheck = false)
1575  {
1576  if ($this->versioningPreview && is_array($row)) {
1577  // will overlay any movePlhOL found with the real record, which in turn
1578  // will be overlaid with its workspace version if any.
1579  $movePldSwap = $this->movePlhOL($table, $row);
1580  // implode(',',array_keys($row)) = Using fields from original record to make
1581  // sure no additional fields are selected. This is best for eg. getPageOverlay()
1582  // Computed properties are excluded since those would lead to SQL errors.
1583  $fieldNames = implode(',', array_keys($this->purgeComputedProperties($row)));
1584  if ($wsAlt = $this->getWorkspaceVersionOfRecord($this->versioningWorkspaceId, $table, $row['uid'], $fieldNames, $bypassEnableFieldsCheck)) {
1585  if (is_array($wsAlt)) {
1586  // Always fix PID (like in fixVersioningPid() above). [This is usually not
1587  // the important factor for versioning OL]
1588  // Keep the old (-1) - indicates it was a version...
1589  $wsAlt['_ORIG_pid'] = $wsAlt['pid'];
1590  // Set in the online versions PID.
1591  $wsAlt['pid'] = $row['pid'];
1592  // For versions of single elements or page+content, preserve online UID and PID
1593  // (this will produce true "overlay" of element _content_, not any references)
1594  // For page+content the "_ORIG_uid" should actually be used as PID for selection.
1595  $wsAlt['_ORIG_uid'] = $wsAlt['uid'];
1596  $wsAlt['uid'] = $row['uid'];
1597  // Translate page alias as well so links are pointing to the _online_ page:
1598  if ($table === 'pages') {
1599  $wsAlt['alias'] = $row['alias'];
1600  }
1601  // Changing input record to the workspace version alternative:
1602  $row = $wsAlt;
1603  // Check if it is deleted/new
1604  $rowVersionState = VersionState::cast($row['t3ver_state']);
1605  if (
1606  $rowVersionState->equals(VersionState::NEW_PLACEHOLDER)
1607  || $rowVersionState->equals(VersionState::DELETE_PLACEHOLDER)
1608  ) {
1609  // Unset record if it turned out to be deleted in workspace
1610  $row = false;
1611  }
1612  // Check if move-pointer in workspace (unless if a move-placeholder is the
1613  // reason why it appears!):
1614  // You have to specifically set $unsetMovePointers in order to clear these
1615  // because it is normally a display issue if it should be shown or not.
1616  if (
1617  (
1618  $rowVersionState->equals(VersionState::MOVE_POINTER)
1619  && !$movePldSwap
1620  ) && $unsetMovePointers
1621  ) {
1622  // Unset record if it turned out to be deleted in workspace
1623  $row = false;
1624  }
1625  } else {
1626  // No version found, then check if t3ver_state = VersionState::NEW_PLACEHOLDER
1627  // (online version is dummy-representation)
1628  // Notice, that unless $bypassEnableFieldsCheck is TRUE, the $row is unset if
1629  // enablefields for BOTH the version AND the online record deselects it. See
1630  // note for $bypassEnableFieldsCheck
1632  $versionState = VersionState::cast($row['t3ver_state']);
1633  if ($wsAlt <= -1 || $versionState->indicatesPlaceholder()) {
1634  // Unset record if it turned out to be "hidden"
1635  $row = false;
1636  }
1637  }
1638  }
1639  }
1640  }
1641 
1652  public function movePlhOL($table, &$row)
1653  {
1654  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
1655  && (int)VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)
1656  ) {
1657  // If t3ver_move_id is not found, then find it (but we like best if it is here)
1658  if (!isset($row['t3ver_move_id'])) {
1659  $moveIDRec = $this->getRawRecord($table, $row['uid'], 't3ver_move_id', true);
1660  $moveID = $moveIDRec['t3ver_move_id'];
1661  } else {
1662  $moveID = $row['t3ver_move_id'];
1663  }
1664  // Find pointed-to record.
1665  if ($moveID) {
1666  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1667  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1668  $origRow = $queryBuilder->select(...array_keys($this->purgeComputedProperties($row)))
1669  ->from($table)
1670  ->where(
1671  $queryBuilder->expr()->eq(
1672  'uid',
1673  $queryBuilder->createNamedParameter($moveID, \PDO::PARAM_INT)
1674  )
1675  )
1676  ->setMaxResults(1)
1677  ->execute()
1678  ->fetch();
1679 
1680  if ($origRow) {
1681  $row = $origRow;
1682  return true;
1683  }
1684  }
1685  }
1686  return false;
1687  }
1688 
1698  public function getMovePlaceholder($table, $uid, $fields = '*')
1699  {
1700  if ($this->versioningPreview) {
1701  $workspace = (int)$this->versioningWorkspaceId;
1702  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $workspace !== 0) {
1703  // Select workspace version of record:
1704  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1705  $queryBuilder->getRestrictions()
1706  ->removeAll()
1707  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1708 
1709  $row = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1710  ->from($table)
1711  ->where(
1712  $queryBuilder->expr()->neq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1713  $queryBuilder->expr()->eq(
1714  't3ver_state',
1715  $queryBuilder->createNamedParameter(
1717  \PDO::PARAM_INT
1718  )
1719  ),
1720  $queryBuilder->expr()->eq(
1721  't3ver_move_id',
1722  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1723  ),
1724  $queryBuilder->expr()->eq(
1725  't3ver_wsid',
1726  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1727  )
1728  )
1729  ->setMaxResults(1)
1730  ->execute()
1731  ->fetch();
1732 
1733  if (is_array($row)) {
1734  return $row;
1735  }
1736  }
1737  }
1738  return false;
1739  }
1740 
1752  public function getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields = '*', $bypassEnableFieldsCheck = false)
1753  {
1754  if ($workspace !== 0 && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
1755  $workspace = (int)$workspace;
1756  $uid = (int)$uid;
1757  // Select workspace version of record, only testing for deleted.
1758  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1759  $queryBuilder->getRestrictions()
1760  ->removeAll()
1761  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1762 
1763  $newrow = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields, true))
1764  ->from($table)
1765  ->where(
1766  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1767  $queryBuilder->expr()->eq(
1768  't3ver_oid',
1769  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1770  ),
1771  $queryBuilder->expr()->eq(
1772  't3ver_wsid',
1773  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1774  )
1775  )
1776  ->setMaxResults(1)
1777  ->execute()
1778  ->fetch();
1779 
1780  // If version found, check if it could have been selected with enableFields on
1781  // as well:
1782  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
1783  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
1784  // Remove the frontend workspace restriction because we are testing a version record
1785  $queryBuilder->getRestrictions()->removeByType(FrontendWorkspaceRestriction::class);
1786  $queryBuilder->select('uid')
1787  ->from($table)
1788  ->setMaxResults(1);
1789 
1790  if (is_array($newrow)) {
1791  $queryBuilder->where(
1792  $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)),
1793  $queryBuilder->expr()->eq(
1794  't3ver_oid',
1795  $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT)
1796  ),
1797  $queryBuilder->expr()->eq(
1798  't3ver_wsid',
1799  $queryBuilder->createNamedParameter($workspace, \PDO::PARAM_INT)
1800  )
1801  );
1802  if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
1803  // Return offline version, tested for its enableFields.
1804  return $newrow;
1805  }
1806  // Return -1 because offline version was de-selected due to its enableFields.
1807  return -1;
1808  }
1809  // OK, so no workspace version was found. Then check if online version can be
1810  // selected with full enable fields and if so, return 1:
1811  $queryBuilder->where(
1812  $queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($uid, \PDO::PARAM_INT))
1813  );
1814  if ($bypassEnableFieldsCheck || $queryBuilder->execute()->fetchColumn()) {
1815  // Means search was done, but no version found.
1816  return 1;
1817  }
1818  // Return -2 because the online record was de-selected due to its enableFields.
1819  return -2;
1820  }
1821  // No look up in database because versioning not enabled / or workspace not
1822  // offline
1823  return false;
1824  }
1825 
1832  public function checkWorkspaceAccess($wsid)
1833  {
1834  if (!$this->getBackendUser() || !ExtensionManagementUtility::isLoaded('workspaces')) {
1835  return false;
1836  }
1837  if (!isset($this->workspaceCache[$wsid])) {
1838  $this->workspaceCache[$wsid] = $this->getBackendUser()->checkWorkspace($wsid);
1839  }
1840  return (string)$this->workspaceCache[$wsid]['_ACCESS'] !== '';
1841  }
1842 
1851  public function getFileReferences($tableName, $fieldName, array $element)
1852  {
1854  $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
1855  $currentId = !empty($element['uid']) ? $element['uid'] : 0;
1856 
1857  // Fetch the references of the default element
1858  try {
1859  $references = $fileRepository->findByRelation($tableName, $fieldName, $currentId);
1860  } catch (FileDoesNotExistException $e) {
1865  return [];
1866  } catch (\InvalidArgumentException $e) {
1871  $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')';
1872  GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1873  return [];
1874  }
1875 
1876  $localizedId = null;
1877  if (isset($element['_LOCALIZED_UID'])) {
1878  $localizedId = $element['_LOCALIZED_UID'];
1879  } elseif (isset($element['_PAGES_OVERLAY_UID'])) {
1880  $localizedId = $element['_PAGES_OVERLAY_UID'];
1881  }
1882 
1883  if ($tableName === 'pages') {
1884  $tableName = 'pages_language_overlay';
1885  }
1886 
1887  $isTableLocalizable = (
1888  !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1889  && !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1890  // Only fetch references if the field is defined in TCA. This is a special use-case
1891  // for pages_language_overlay because it may be possible that a field is defined in TCA
1892  // of "pages" but not in "pages_language_overlay". Once pages_language_overlay is removed
1893  // this check can be removed as well
1894  && isset($GLOBALS['TCA'][$tableName]['columns'][$fieldName])
1895  );
1896  if ($isTableLocalizable && $localizedId !== null) {
1897  $localizedReferences = $fileRepository->findByRelation($tableName, $fieldName, $localizedId);
1898  $references = $localizedReferences;
1899  }
1900 
1901  return $references;
1902  }
1903 
1911  protected function purgeComputedProperties(array $row)
1912  {
1913  foreach ($this->computedPropertyNames as $computedPropertyName) {
1914  if (array_key_exists($computedPropertyName, $row)) {
1915  unset($row[$computedPropertyName]);
1916  }
1917  }
1918  return $row;
1919  }
1920 
1930  protected function shouldFieldBeOverlaid($table, $field, $value)
1931  {
1933  return true;
1934  }
1935 
1939  protected function getTypoScriptFrontendController()
1940  {
1941  return $GLOBALS['TSFE'];
1942  }
1943 
1949  protected function getBackendUser()
1950  {
1951  return $GLOBALS['BE_USER'];
1952  }
1953 }
getRecordOverlay($table, $row, $sys_language_content, $OLmode='')
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
getRecordsByField($theTable, $theField, $theValue, $whereClause='', $groupBy='', $orderBy='', $limit='')
getRawRecord($table, $uid, $fields=' *', $noWSOL=false)
getRootLine($uid, $MP='', $ignoreMPerrors=false)
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
getMenu($pageId, $fields=' *', $sortField='sorting', $additionalWhereClause='', $checkShortcuts=true)
getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields=' *', $bypassEnableFieldsCheck=false)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
getDomainStartPage($domain, $path='', $request_uri='')
static makeInstance($className,... $constructorArguments)
$fields
Definition: pages.php:4
getSubpagesForPages(array $pageIds, $fields=' *', $sortField='sorting', $additionalWhereClause='', $checkShortcuts=true, $parentPages=true)
static storeHash($hash, $data, $ident, $lifetime=0)
shouldFieldBeOverlaid($table, $field, $value)
getMovePlaceholder($table, $uid, $fields=' *')
getPagesOverlay(array $pagesInput, $lUid=-1)
checkRecord($table, $uid, $checkPage=0)
getMenuForPages(array $pageIds, $fields=' *', $sortField='sorting', $additionalWhereClause='', $checkShortcuts=true)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
static stripLogicalOperatorPrefix(string $constraint)
static fixed_lgd_cs($string, $chars, $appendString='...')
checkValidShortcutOfPage(array $page, $additionalWhereClause)
getPage($uid, $disableGroupAccessCheck=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
enableFields($table, $show_hidden=-1, $ignore_array=[], $noVersionPreview=false)
getMountPointInfo($pageId, $pageRec=false, $prevMountPids=[], $firstPageUid=0)