TYPO3  7.6
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 
18 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
25 
35 {
39  public $urltypes = array('', 'http://', 'ftp://', 'mailto:', 'https://');
40 
48  public $where_hid_del = ' AND pages.deleted=0';
49 
55  public $where_groupAccess = '';
56 
60  public $sys_language_uid = 0;
61 
69  public $versioningPreview = false;
70 
75 
82 
86  public $workspaceCache = array();
87 
93  public $error_getRootLine = '';
94 
101 
105  protected $cache_getRootLine = array();
106 
110  protected $cache_getPage = array();
111 
115  protected $cache_getPage_noCheck = array();
116 
120  protected $cache_getPageIdFromAlias = array();
121 
125  protected $cache_getMountPointInfo = array();
126 
130  protected $tableNamesAllowedOnRootLevel = array(
131  'sys_file_metadata',
132  'sys_category',
133  );
134 
140  protected $computedPropertyNames = array(
141  '_LOCALIZED_UID',
142  '_MP_PARAM',
143  '_ORIG_uid',
144  '_ORIG_pid',
145  '_PAGES_OVERLAY',
146  '_PAGES_OVERLAY_UID',
147  '_PAGES_OVERLAY_LANGUAGE',
148  );
149 
153  const DOKTYPE_DEFAULT = 1;
154  const DOKTYPE_LINK = 3;
155  const DOKTYPE_SHORTCUT = 4;
158  const DOKTYPE_SPACER = 199;
159  const DOKTYPE_SYSFOLDER = 254;
160  const DOKTYPE_RECYCLER = 255;
161 
169 
180  public function init($show_hidden)
181  {
182  $this->where_groupAccess = '';
183 
184  if ($this->versioningPreview) {
185  // For version previewing, make sure that enable-fields are not
186  // de-selecting hidden pages - we need versionOL() to unset them only
187  // if the overlay record instructs us to.
188  // Clear where_hid_del and restrict to live and current workspaces
189  $this->where_hid_del = ' AND pages.deleted=0 AND (pages.t3ver_wsid=0 OR pages.t3ver_wsid=' . (int)$this->versioningWorkspaceId . ')';
190  } else {
191  // add starttime / endtime, and check for hidden/deleted
192  // Filter out new/deleted place-holder pages in case we are NOT in a
193  // versioning preview (that means we are online!)
194  $this->where_hid_del = $this->enableFields('pages', $show_hidden, array('fe_group' => true), true);
195  }
196  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][PageRepository::class]['init'])) {
197  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][PageRepository::class]['init'] as $classRef) {
198  $hookObject = GeneralUtility::makeInstance($classRef);
199  if (!$hookObject instanceof PageRepositoryInitHookInterface) {
200  throw new \UnexpectedValueException($hookObject . ' must implement interface TYPO3\\CMS\\Frontend\\Page\\PageRepositoryInitHookInterface', 1379579812);
201  }
202  $hookObject->init_postProcess($this);
203  }
204  }
205  }
206 
207  /**************************
208  *
209  * Selecting page records
210  *
211  **************************/
212 
224  public function getPage($uid, $disableGroupAccessCheck = false)
225  {
226  // Hook to manipulate the page uid for special overlay handling
227  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'])) {
228  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPage'] as $classRef) {
229  $hookObject = GeneralUtility::getUserObj($classRef);
230  if (!$hookObject instanceof PageRepositoryGetPageHookInterface) {
231  throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetPageHookInterface::class, 1251476766);
232  }
233  $hookObject->getPage_preProcess($uid, $disableGroupAccessCheck, $this);
234  }
235  }
236  $accessCheck = $disableGroupAccessCheck ? '' : $this->where_groupAccess;
237  $cacheKey = md5($accessCheck . '-' . $this->where_hid_del . '-' . $this->sys_language_uid);
238  if (is_array($this->cache_getPage[$uid][$cacheKey])) {
239  return $this->cache_getPage[$uid][$cacheKey];
240  }
241  $result = array();
242  $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'pages', 'uid=' . (int)$uid . $this->where_hid_del . $accessCheck);
243  if ($row) {
244  $this->versionOL('pages', $row);
245  if (is_array($row)) {
246  $result = $this->getPageOverlay($row);
247  }
248  }
249  $this->cache_getPage[$uid][$cacheKey] = $result;
250  return $result;
251  }
252 
261  public function getPage_noCheck($uid)
262  {
263  if ($this->cache_getPage_noCheck[$uid]) {
264  return $this->cache_getPage_noCheck[$uid];
265  }
266  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'pages', 'uid=' . (int)$uid . $this->deleteClause('pages'));
267  $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
268  $this->getDatabaseConnection()->sql_free_result($res);
269  $result = array();
270  if ($row) {
271  $this->versionOL('pages', $row);
272  if (is_array($row)) {
273  $result = $this->getPageOverlay($row);
274  }
275  }
276  $this->cache_getPage_noCheck[$uid] = $result;
277  return $result;
278  }
279 
287  public function getFirstWebPage($uid)
288  {
289  $output = '';
290  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'pages', 'pid=' . (int)$uid . $this->where_hid_del . $this->where_groupAccess, '', 'sorting', '1');
291  $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
292  $this->getDatabaseConnection()->sql_free_result($res);
293  if ($row) {
294  $this->versionOL('pages', $row);
295  if (is_array($row)) {
296  $output = $this->getPageOverlay($row);
297  }
298  }
299  return $output;
300  }
301 
309  public function getPageIdFromAlias($alias)
310  {
311  $alias = strtolower($alias);
312  if ($this->cache_getPageIdFromAlias[$alias]) {
313  return $this->cache_getPageIdFromAlias[$alias];
314  }
315  $db = $this->getDatabaseConnection();
316  $row = $db->exec_SELECTgetSingleRow('uid', 'pages', 'alias=' . $db->fullQuoteStr($alias, 'pages') . ' AND pid>=0 AND pages.deleted=0');
317  // "AND pid>=0" because of versioning (means that aliases sent MUST be online!)
318  if ($row) {
319  $this->cache_getPageIdFromAlias[$alias] = $row['uid'];
320  return $row['uid'];
321  }
322  $this->cache_getPageIdFromAlias[$alias] = 0;
323  return 0;
324  }
325 
334  public function getPageOverlay($pageInput, $lUid = -1)
335  {
336  $rows = $this->getPagesOverlay(array($pageInput), $lUid);
337  // Always an array in return
338  return isset($rows[0]) ? $rows[0] : array();
339  }
340 
352  public function getPagesOverlay(array $pagesInput, $lUid = -1)
353  {
354  if (empty($pagesInput)) {
355  return array();
356  }
357  // Initialize:
358  if ($lUid < 0) {
359  $lUid = $this->sys_language_uid;
360  }
361  $row = null;
362  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'])) {
363  foreach ($pagesInput as $origPage) {
364  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getPageOverlay'] as $classRef) {
365  $hookObject = GeneralUtility::getUserObj($classRef);
366  if (!$hookObject instanceof PageRepositoryGetPageOverlayHookInterface) {
367  throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetPageOverlayHookInterface::class, 1269878881);
368  }
369  $hookObject->getPageOverlay_preProcess($origPage, $lUid, $this);
370  }
371  }
372  }
373  // If language UID is different from zero, do overlay:
374  if ($lUid) {
375  $fieldArr = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['pageOverlayFields'], true);
376  $page_ids = array();
377 
378  $origPage = reset($pagesInput);
379  if (is_array($origPage)) {
380  // Make sure that only fields which exist in the first incoming record are overlaid!
381  $fieldArr = array_intersect($fieldArr, array_keys($origPage));
382  }
383  foreach ($pagesInput as $origPage) {
384  if (is_array($origPage)) {
385  // Was the whole record
386  $page_ids[] = $origPage['uid'];
387  } else {
388  // Was the id
389  $page_ids[] = $origPage;
390  }
391  }
392  if (!empty($fieldArr)) {
393  if (!in_array('pid', $fieldArr, true)) {
394  $fieldArr[] = 'pid';
395  }
396  // NOTE to enabledFields('pages_language_overlay'):
397  // Currently the showHiddenRecords of TSFE set will allow
398  // pages_language_overlay records to be selected as they are
399  // child-records of a page.
400  // However you may argue that the showHiddenField flag should
401  // determine this. But that's not how it's done right now.
402  // Selecting overlay record:
403  $db = $this->getDatabaseConnection();
404  $res = $db->exec_SELECTquery(
405  implode(',', $fieldArr),
406  'pages_language_overlay',
407  'pid IN(' . implode(',', $db->cleanIntArray($page_ids)) . ')'
408  . ' AND sys_language_uid=' . (int)$lUid . $this->enableFields('pages_language_overlay')
409  );
410  $overlays = array();
411  while ($row = $db->sql_fetch_assoc($res)) {
412  $this->versionOL('pages_language_overlay', $row);
413  if (is_array($row)) {
414  $row['_PAGES_OVERLAY'] = true;
415  $row['_PAGES_OVERLAY_UID'] = $row['uid'];
416  $row['_PAGES_OVERLAY_LANGUAGE'] = $lUid;
417  $origUid = $row['pid'];
418  // Unset vital fields that are NOT allowed to be overlaid:
419  unset($row['uid']);
420  unset($row['pid']);
421  $overlays[$origUid] = $row;
422  }
423  }
424  $db->sql_free_result($res);
425  }
426  }
427  // Create output:
428  $pagesOutput = array();
429  foreach ($pagesInput as $key => $origPage) {
430  if (is_array($origPage)) {
431  $pagesOutput[$key] = $origPage;
432  if (isset($overlays[$origPage['uid']])) {
433  // Overwrite the original field with the overlay
434  foreach ($overlays[$origPage['uid']] as $fieldName => $fieldValue) {
435  if ($fieldName !== 'uid' && $fieldName !== 'pid') {
436  if ($this->shouldFieldBeOverlaid('pages_language_overlay', $fieldName, $fieldValue)) {
437  $pagesOutput[$key][$fieldName] = $fieldValue;
438  }
439  }
440  }
441  }
442  } else {
443  if (isset($overlays[$origPage])) {
444  $pagesOutput[$key] = $overlays[$origPage];
445  }
446  }
447  }
448  return $pagesOutput;
449  }
450 
462  public function getRecordOverlay($table, $row, $sys_language_content, $OLmode = '')
463  {
464  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
465  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
466  $hookObject = GeneralUtility::getUserObj($classRef);
467  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
468  throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881658);
469  }
470  $hookObject->getRecordOverlay_preProcess($table, $row, $sys_language_content, $OLmode, $this);
471  }
472  }
473  if ($row['uid'] > 0 && ($row['pid'] > 0 || in_array($table, $this->tableNamesAllowedOnRootLevel, true))) {
474  if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']) {
475  if (!$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable']) {
476  // Will not be able to work with other tables (Just didn't implement it yet;
477  // Requires a scan over all tables [ctrl] part for first FIND the table that
478  // carries localization information for this table (which could even be more
479  // than a single table) and then use that. Could be implemented, but obviously
480  // takes a little more....) Will try to overlay a record only if the
481  // sys_language_content value is larger than zero.
482  if ($sys_language_content > 0) {
483  // Must be default language or [All], otherwise no overlaying:
484  if ((int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
485  // Select overlay record:
486  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $table, 'pid=' . (int)$row['pid'] . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '=' . (int)$sys_language_content . ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . '=' . (int)$row['uid'] . $this->enableFields($table), '', '', '1');
487  $olrow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
488  $this->getDatabaseConnection()->sql_free_result($res);
489  $this->versionOL($table, $olrow);
490  // Merge record content by traversing all fields:
491  if (is_array($olrow)) {
492  if (isset($olrow['_ORIG_uid'])) {
493  $row['_ORIG_uid'] = $olrow['_ORIG_uid'];
494  }
495  if (isset($olrow['_ORIG_pid'])) {
496  $row['_ORIG_pid'] = $olrow['_ORIG_pid'];
497  }
498  foreach ($row as $fN => $fV) {
499  if ($fN !== 'uid' && $fN !== 'pid' && isset($olrow[$fN])) {
500  if ($this->shouldFieldBeOverlaid($table, $fN, $olrow[$fN])) {
501  $row[$fN] = $olrow[$fN];
502  }
503  } elseif ($fN === 'uid') {
504  $row['_LOCALIZED_UID'] = $olrow['uid'];
505  }
506  }
507  } elseif ($OLmode === 'hideNonTranslated' && (int)$row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] === 0) {
508  // Unset, if non-translated records should be hidden. ONLY done if the source
509  // record really is default language and not [All] in which case it is allowed.
510  unset($row);
511  }
512  } elseif ($sys_language_content != $row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]) {
513  unset($row);
514  }
515  } else {
516  // When default language is displayed, we never want to return a record carrying
517  // another language!
518  if ($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']] > 0) {
519  unset($row);
520  }
521  }
522  }
523  }
524  }
525  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'])) {
526  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['getRecordOverlay'] as $classRef) {
527  $hookObject = GeneralUtility::getUserObj($classRef);
528  if (!$hookObject instanceof PageRepositoryGetRecordOverlayHookInterface) {
529  throw new \UnexpectedValueException('$hookObject must implement interface ' . PageRepositoryGetRecordOverlayHookInterface::class, 1269881659);
530  }
531  $hookObject->getRecordOverlay_postProcess($table, $row, $sys_language_content, $OLmode, $this);
532  }
533  }
534  return $row;
535  }
536 
537  /************************************************
538  *
539  * Page related: Menu, Domain record, Root line
540  *
541  ************************************************/
542 
559  public function getMenu($pageId, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
560  {
561  return $this->getSubpagesForPages((array)$pageId, $fields, $sortField, $additionalWhereClause, $checkShortcuts);
562  }
563 
577  public function getMenuForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true)
578  {
579  return $this->getSubpagesForPages($pageIds, $fields, $sortField, $additionalWhereClause, $checkShortcuts, false);
580  }
581 
601  protected function getSubpagesForPages(array $pageIds, $fields = '*', $sortField = 'sorting', $additionalWhereClause = '', $checkShortcuts = true, $parentPages = true)
602  {
603  $pages = [];
604  $relationField = $parentPages ? 'pid' : 'uid';
605  $db = $this->getDatabaseConnection();
606 
607  $whereStatement = $relationField . ' IN ('
608  . implode(',', $db->cleanIntArray($pageIds)) . ')'
609  . $this->where_hid_del
610  . $this->where_groupAccess
611  . ' '
612  . $additionalWhereClause;
613 
614  $databaseResource = $db->exec_SELECTquery(
615  $fields,
616  'pages',
617  $whereStatement,
618  '',
619  $sortField
620  );
621 
622  while (($page = $db->sql_fetch_assoc($databaseResource))) {
623  $originalUid = $page['uid'];
624 
625  // Versioning Preview Overlay
626  $this->versionOL('pages', $page, true);
627 
628  // Add a mount point parameter if needed
629  $page = $this->addMountPointParameterToPage((array)$page);
630 
631  // If shortcut, look up if the target exists and is currently visible
632  if ($checkShortcuts) {
633  $page = $this->checkValidShortcutOfPage((array)$page, $additionalWhereClause);
634  }
635 
636  // If the page still is there, we add it to the output
637  if (!empty($page)) {
638  $pages[$originalUid] = $page;
639  }
640  }
641 
642  $db->sql_free_result($databaseResource);
643 
644  // Finally load language overlays
645  return $this->getPagesOverlay($pages);
646  }
647 
654  protected function addMountPointParameterToPage(array $page)
655  {
656  if (empty($page)) {
657  return [];
658  }
659 
660  // $page MUST have "uid", "pid", "doktype", "mount_pid", "mount_pid_ol" fields in it
661  $mountPointInfo = $this->getMountPointInfo($page['uid'], $page);
662 
663  // There is a valid mount point.
664  if (is_array($mountPointInfo) && $mountPointInfo['overlay']) {
665 
666  // Using "getPage" is OK since we need the check for enableFields AND for type 2
667  // of mount pids we DO require a doktype < 200!
668  $mountPointPage = $this->getPage($mountPointInfo['mount_pid']);
669 
670  if (!empty($mountPointPage)) {
671  $page = $mountPointPage;
672  $page['_MP_PARAM'] = $mountPointInfo['MPvar'];
673  } else {
674  $page = [];
675  }
676  }
677  return $page;
678  }
679 
687  protected function checkValidShortcutOfPage(array $page, $additionalWhereClause)
688  {
689  if (empty($page)) {
690  return [];
691  }
692 
693  $dokType = (int)$page['doktype'];
694  $shortcutMode = (int)$page['shortcut_mode'];
695 
696  if ($dokType === self::DOKTYPE_SHORTCUT && ($page['shortcut'] || $shortcutMode)) {
697  if ($shortcutMode === self::SHORTCUT_MODE_NONE) {
698  // No shortcut_mode set, so target is directly set in $page['shortcut']
699  $searchField = 'uid';
700  $searchUid = (int)$page['shortcut'];
701  } elseif ($shortcutMode === self::SHORTCUT_MODE_FIRST_SUBPAGE || $shortcutMode === self::SHORTCUT_MODE_RANDOM_SUBPAGE) {
702  // Check subpages - first subpage or random subpage
703  $searchField = 'pid';
704  // If a shortcut mode is set and no valid page is given to select subpags
705  // from use the actual page.
706  $searchUid = (int)$page['shortcut'] ?: $page['uid'];
707  } elseif ($shortcutMode === self::SHORTCUT_MODE_PARENT_PAGE) {
708  // Shortcut to parent page
709  $searchField = 'uid';
710  $searchUid = $page['pid'];
711  } else {
712  $searchField = '';
713  $searchUid = 0;
714  }
715 
716  $whereStatement = $searchField . '=' . $searchUid
717  . $this->where_hid_del
718  . $this->where_groupAccess
719  . ' ' . $additionalWhereClause;
720 
721  $count = $this->getDatabaseConnection()->exec_SELECTcountRows(
722  'uid',
723  'pages',
724  $whereStatement
725  );
726 
727  if (!$count) {
728  $page = [];
729  }
730  } elseif ($dokType === self::DOKTYPE_SHORTCUT) {
731  // Neither shortcut target nor mode is set. Remove the page from the menu.
732  $page = [];
733  }
734  return $page;
735  }
747  public function getDomainStartPage($domain, $path = '', $request_uri = '')
748  {
749  $domain = explode(':', $domain);
750  $domain = strtolower(preg_replace('/\\.$/', '', $domain[0]));
751  // Removing extra trailing slashes
752  $path = trim(preg_replace('/\\/[^\\/]*$/', '', $path));
753  // Appending to domain string
754  $domain .= $path;
755  $domain = preg_replace('/\\/*$/', '', $domain);
756  $res = $this->getDatabaseConnection()->exec_SELECTquery('pages.uid,sys_domain.redirectTo,sys_domain.redirectHttpStatusCode,sys_domain.prepend_params', 'pages,sys_domain', 'pages.uid=sys_domain.pid
757  AND sys_domain.hidden=0
758  AND (sys_domain.domainName=' . $this->getDatabaseConnection()->fullQuoteStr($domain, 'sys_domain') . ' OR sys_domain.domainName=' . $this->getDatabaseConnection()->fullQuoteStr(($domain . '/'), 'sys_domain') . ') ' . $this->where_hid_del . $this->where_groupAccess, '', '', 1);
759  $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
760  $this->getDatabaseConnection()->sql_free_result($res);
761  if ($row) {
762  if ($row['redirectTo']) {
763  $redirectUrl = $row['redirectTo'];
764  if ($row['prepend_params']) {
765  $redirectUrl = rtrim($redirectUrl, '/');
766  $prependStr = ltrim(substr($request_uri, strlen($path)), '/');
767  $redirectUrl .= '/' . $prependStr;
768  }
769  $statusCode = (int)$row['redirectHttpStatusCode'];
770  if ($statusCode && defined(HttpUtility::class . '::HTTP_STATUS_' . $statusCode)) {
771  HttpUtility::redirect($redirectUrl, constant(HttpUtility::class . '::HTTP_STATUS_' . $statusCode));
772  } else {
774  }
775  die;
776  } else {
777  return $row['uid'];
778  }
779  }
780  return '';
781  }
782 
803  public function getRootLine($uid, $MP = '', $ignoreMPerrors = false)
804  {
805  $rootline = GeneralUtility::makeInstance(RootlineUtility::class, $uid, $MP, $this);
806  try {
807  return $rootline->get();
808  } catch (\RuntimeException $ex) {
809  if ($ignoreMPerrors) {
810  $this->error_getRootLine = $ex->getMessage();
811  if (substr($this->error_getRootLine, -7) === 'uid -1.') {
812  $this->error_getRootLine_failPid = -1;
813  }
814  return array();
816  } elseif ($ex->getCode() === 1343589451) {
817  return array();
818  }
819  throw $ex;
820  }
821  }
822 
832  public function getPathFromRootline($rl, $len = 20)
833  {
834  $path = '';
835  if (is_array($rl)) {
836  $c = count($rl);
837  for ($a = 0; $a < $c; $a++) {
838  if ($rl[$a]['uid']) {
839  $path .= '/' . GeneralUtility::fixed_lgd_cs(strip_tags($rl[$a]['title']), $len);
840  }
841  }
842  }
843  return $path;
844  }
845 
855  public function getExtURL($pagerow, $disable = false)
856  {
857  if ($disable !== false) {
858  GeneralUtility::deprecationLog('The disable option of PageRepository::getExtUrl() is deprecated since TYPO3 CMS 7, will be removed with TYPO3 CMS 8.');
859  return false;
860  }
861  if ((int)$pagerow['doktype'] === self::DOKTYPE_LINK) {
862  $redirectTo = $this->urltypes[$pagerow['urltype']] . $pagerow['url'];
863  // If relative path, prefix Site URL:
864  $uI = parse_url($redirectTo);
865  // Relative path assumed now.
866  if (!$uI['scheme'] && $redirectTo[0] !== '/') {
867  $redirectTo = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $redirectTo;
868  }
869  return $redirectTo;
870  }
871  return false;
872  }
873 
887  public function getMountPointInfo($pageId, $pageRec = false, $prevMountPids = array(), $firstPageUid = 0)
888  {
889  $result = false;
890  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) {
891  if (isset($this->cache_getMountPointInfo[$pageId])) {
892  return $this->cache_getMountPointInfo[$pageId];
893  }
894  // Get pageRec if not supplied:
895  if (!is_array($pageRec)) {
896  $res = $this->getDatabaseConnection()->exec_SELECTquery('uid,pid,doktype,mount_pid,mount_pid_ol,t3ver_state', 'pages', 'uid=' . (int)$pageId . ' AND pages.deleted=0 AND pages.doktype<>255');
897  $pageRec = $this->getDatabaseConnection()->sql_fetch_assoc($res);
898  $this->getDatabaseConnection()->sql_free_result($res);
899  // Only look for version overlay if page record is not supplied; This assumes
900  // that the input record is overlaid with preview version, if any!
901  $this->versionOL('pages', $pageRec);
902  }
903  // Set first Page uid:
904  if (!$firstPageUid) {
905  $firstPageUid = $pageRec['uid'];
906  }
907  // Look for mount pid value plus other required circumstances:
908  $mount_pid = (int)$pageRec['mount_pid'];
909  if (is_array($pageRec) && (int)$pageRec['doktype'] === self::DOKTYPE_MOUNTPOINT && $mount_pid > 0 && !in_array($mount_pid, $prevMountPids, true)) {
910  // Get the mount point record (to verify its general existence):
911  $res = $this->getDatabaseConnection()->exec_SELECTquery('uid,pid,doktype,mount_pid,mount_pid_ol,t3ver_state', 'pages', 'uid=' . $mount_pid . ' AND pages.deleted=0 AND pages.doktype<>255');
912  $mountRec = $this->getDatabaseConnection()->sql_fetch_assoc($res);
913  $this->getDatabaseConnection()->sql_free_result($res);
914  $this->versionOL('pages', $mountRec);
915  if (is_array($mountRec)) {
916  // Look for recursive mount point:
917  $prevMountPids[] = $mount_pid;
918  $recursiveMountPid = $this->getMountPointInfo($mount_pid, $mountRec, $prevMountPids, $firstPageUid);
919  // Return mount point information:
920  $result = $recursiveMountPid ?: array(
921  'mount_pid' => $mount_pid,
922  'overlay' => $pageRec['mount_pid_ol'],
923  'MPvar' => $mount_pid . '-' . $firstPageUid,
924  'mount_point_rec' => $pageRec,
925  'mount_pid_rec' => $mountRec
926  );
927  } else {
928  // Means, there SHOULD have been a mount point, but there was none!
929  $result = -1;
930  }
931  }
932  }
933  $this->cache_getMountPointInfo[$pageId] = $result;
934  return $result;
935  }
936 
937  /********************************
938  *
939  * Selecting records in general
940  *
941  ********************************/
942 
952  public function checkRecord($table, $uid, $checkPage = 0)
953  {
954  $uid = (int)$uid;
955  if (is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
956  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $table, 'uid = ' . $uid . $this->enableFields($table));
957  $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
958  $this->getDatabaseConnection()->sql_free_result($res);
959  if ($row) {
960  $this->versionOL($table, $row);
961  if (is_array($row)) {
962  if ($checkPage) {
963  $res = $this->getDatabaseConnection()->exec_SELECTquery('uid', 'pages', 'uid=' . (int)$row['pid'] . $this->enableFields('pages'));
964  $numRows = $this->getDatabaseConnection()->sql_num_rows($res);
965  $this->getDatabaseConnection()->sql_free_result($res);
966  if ($numRows > 0) {
967  return $row;
968  } else {
969  return 0;
970  }
971  } else {
972  return $row;
973  }
974  }
975  }
976  }
977  return 0;
978  }
979 
990  public function getRawRecord($table, $uid, $fields = '*', $noWSOL = false)
991  {
992  $uid = (int)$uid;
993  if (isset($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]) && $uid > 0) {
994  $res = $this->getDatabaseConnection()->exec_SELECTquery($fields, $table, 'uid = ' . $uid . $this->deleteClause($table));
995  $row = $this->getDatabaseConnection()->sql_fetch_assoc($res);
996  $this->getDatabaseConnection()->sql_free_result($res);
997  if ($row) {
998  if (!$noWSOL) {
999  $this->versionOL($table, $row);
1000  }
1001  if (is_array($row)) {
1002  return $row;
1003  }
1004  }
1005  }
1006  return 0;
1007  }
1008 
1021  public function getRecordsByField($theTable, $theField, $theValue, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
1022  {
1023  if (is_array($GLOBALS['TCA'][$theTable])) {
1024  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', $theTable, $theField . '=' . $this->getDatabaseConnection()->fullQuoteStr($theValue, $theTable) . $this->deleteClause($theTable) . ' ' . $whereClause, $groupBy, $orderBy, $limit);
1025  $rows = array();
1026  while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
1027  if (is_array($row)) {
1028  $rows[] = $row;
1029  }
1030  }
1031  $this->getDatabaseConnection()->sql_free_result($res);
1032  if (!empty($rows)) {
1033  return $rows;
1034  }
1035  }
1036  return null;
1037  }
1038 
1039  /********************************
1040  *
1041  * Caching and standard clauses
1042  *
1043  ********************************/
1044 
1056  public static function getHash($hash)
1057  {
1058  $hashContent = null;
1060  $contentHashCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
1061  $cacheEntry = $contentHashCache->get($hash);
1062  if ($cacheEntry) {
1063  $hashContent = $cacheEntry;
1064  }
1065  return $hashContent;
1066  }
1067 
1082  public static function storeHash($hash, $data, $ident, $lifetime = 0)
1083  {
1084  GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash')->set($hash, $data, array('ident_' . $ident), (int)$lifetime);
1085  }
1086 
1095  public function deleteClause($table)
1096  {
1097  return $GLOBALS['TCA'][$table]['ctrl']['delete'] ? ' AND ' . $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['delete'] . '=0' : '';
1098  }
1099 
1117  public function enableFields($table, $show_hidden = -1, $ignore_array = array(), $noVersionPreview = false)
1118  {
1119  if ($show_hidden === -1 && is_object($this->getTypoScriptFrontendController())) {
1120  // If show_hidden was not set from outside and if TSFE is an object, set it
1121  // based on showHiddenPage and showHiddenRecords from TSFE
1122  $show_hidden = $table === 'pages' ? $this->getTypoScriptFrontendController()->showHiddenPage : $this->getTypoScriptFrontendController()->showHiddenRecords;
1123  }
1124  if ($show_hidden === -1) {
1125  $show_hidden = 0;
1126  }
1127  // If show_hidden was not changed during the previous evaluation, do it here.
1128  $ctrl = $GLOBALS['TCA'][$table]['ctrl'];
1129  $query = '';
1130  if (is_array($ctrl)) {
1131  // Delete field check:
1132  if ($ctrl['delete']) {
1133  $query .= ' AND ' . $table . '.' . $ctrl['delete'] . '=0';
1134  }
1135  if ($ctrl['versioningWS']) {
1136  if (!$this->versioningPreview) {
1137  // Filter out placeholder records (new/moved/deleted items)
1138  // in case we are NOT in a versioning preview (that means we are online!)
1139  $query .= ' AND ' . $table . '.t3ver_state<=' . new VersionState(VersionState::DEFAULT_STATE);
1140  } elseif ($table !== 'pages') {
1141  // show only records of live and of the current workspace
1142  // in case we are in a versioning preview
1143  $query .= ' AND (' .
1144  $table . '.t3ver_wsid=0 OR ' .
1145  $table . '.t3ver_wsid=' . (int)$this->versioningWorkspaceId .
1146  ')';
1147  }
1148 
1149  // Filter out versioned records
1150  if (!$noVersionPreview && empty($ignore_array['pid'])) {
1151  $query .= ' AND ' . $table . '.pid<>-1';
1152  }
1153  }
1154 
1155  // Enable fields:
1156  if (is_array($ctrl['enablecolumns'])) {
1157  // In case of versioning-preview, enableFields are ignored (checked in
1158  // versionOL())
1159  if (!$this->versioningPreview || !$ctrl['versioningWS'] || $noVersionPreview) {
1160  if ($ctrl['enablecolumns']['disabled'] && !$show_hidden && !$ignore_array['disabled']) {
1161  $field = $table . '.' . $ctrl['enablecolumns']['disabled'];
1162  $query .= ' AND ' . $field . '=0';
1163  }
1164  if ($ctrl['enablecolumns']['starttime'] && !$ignore_array['starttime']) {
1165  $field = $table . '.' . $ctrl['enablecolumns']['starttime'];
1166  $query .= ' AND ' . $field . '<=' . $GLOBALS['SIM_ACCESS_TIME'];
1167  }
1168  if ($ctrl['enablecolumns']['endtime'] && !$ignore_array['endtime']) {
1169  $field = $table . '.' . $ctrl['enablecolumns']['endtime'];
1170  $query .= ' AND (' . $field . '=0 OR ' . $field . '>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
1171  }
1172  if ($ctrl['enablecolumns']['fe_group'] && !$ignore_array['fe_group']) {
1173  $field = $table . '.' . $ctrl['enablecolumns']['fe_group'];
1174  $query .= $this->getMultipleGroupsWhereClause($field, $table);
1175  }
1176  // Call hook functions for additional enableColumns
1177  // It is used by the extension ingmar_accessctrl which enables assigning more
1178  // than one usergroup to content and page records
1179  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'])) {
1180  $_params = array(
1181  'table' => $table,
1182  'show_hidden' => $show_hidden,
1183  'ignore_array' => $ignore_array,
1184  'ctrl' => $ctrl
1185  );
1186  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_page.php']['addEnableColumns'] as $_funcRef) {
1187  $query .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1188  }
1189  }
1190  }
1191  }
1192  } else {
1193  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);
1194  }
1195  return $query;
1196  }
1197 
1207  public function getMultipleGroupsWhereClause($field, $table)
1208  {
1209  $memberGroups = GeneralUtility::intExplode(',', $this->getTypoScriptFrontendController()->gr_list);
1210  $orChecks = array();
1211  // If the field is empty, then OK
1212  $orChecks[] = $field . '=\'\'';
1213  // If the field is NULL, then OK
1214  $orChecks[] = $field . ' IS NULL';
1215  // If the field contsains zero, then OK
1216  $orChecks[] = $field . '=\'0\'';
1217  foreach ($memberGroups as $value) {
1218  $orChecks[] = $this->getDatabaseConnection()->listQuery($field, $value, $table);
1219  }
1220  return ' AND (' . implode(' OR ', $orChecks) . ')';
1221  }
1222 
1223  /**********************
1224  *
1225  * Versioning Preview
1226  *
1227  **********************/
1228 
1248  public function fixVersioningPid($table, &$rr)
1249  {
1250  if ($this->versioningPreview && is_array($rr) && (int)$rr['pid'] === -1 && $GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1251  $oid = 0;
1252  $wsid = 0;
1253  // Have to hardcode it for "pages" table since TCA is not loaded at this moment!
1254  // Check values for t3ver_oid and t3ver_wsid:
1255  if (isset($rr['t3ver_oid']) && isset($rr['t3ver_wsid'])) {
1256  // If "t3ver_oid" is already a field, just set this:
1257  $oid = $rr['t3ver_oid'];
1258  $wsid = $rr['t3ver_wsid'];
1259  } else {
1260  // Otherwise we have to expect "uid" to be in the record and look up based
1261  // on this:
1262  $newPidRec = $this->getRawRecord($table, $rr['uid'], 't3ver_oid,t3ver_wsid', true);
1263  if (is_array($newPidRec)) {
1264  $oid = $newPidRec['t3ver_oid'];
1265  $wsid = $newPidRec['t3ver_wsid'];
1266  }
1267  }
1268  // If workspace ids matches and ID of current online version is found, look up
1269  // the PID value of that:
1270  if ($oid && ((int)$this->versioningWorkspaceId === 0 && $this->checkWorkspaceAccess($wsid) || (int)$wsid === (int)$this->versioningWorkspaceId)) {
1271  $oidRec = $this->getRawRecord($table, $oid, 'pid', true);
1272  if (is_array($oidRec)) {
1273  // SWAP uid as well? Well no, because when fixing a versioning PID happens it is
1274  // assumed that this is a "branch" type page and therefore the uid should be
1275  // kept (like in versionOL()). However if the page is NOT a branch version it
1276  // should not happen - but then again, direct access to that uid should not
1277  // happen!
1278  $rr['_ORIG_pid'] = $rr['pid'];
1279  $rr['pid'] = $oidRec['pid'];
1280  }
1281  }
1282  }
1283  // Changing PID in case of moving pointer:
1284  if ($movePlhRec = $this->getMovePlaceholder($table, $rr['uid'], 'pid')) {
1285  $rr['pid'] = $movePlhRec['pid'];
1286  }
1287  }
1288 
1309  public function versionOL($table, &$row, $unsetMovePointers = false, $bypassEnableFieldsCheck = false)
1310  {
1311  if ($this->versioningPreview && is_array($row)) {
1312  // will overlay any movePlhOL found with the real record, which in turn
1313  // will be overlaid with its workspace version if any.
1314  $movePldSwap = $this->movePlhOL($table, $row);
1315  // implode(',',array_keys($row)) = Using fields from original record to make
1316  // sure no additional fields are selected. This is best for eg. getPageOverlay()
1317  // Computed properties are excluded since those would lead to SQL errors.
1318  $fieldNames = implode(',', array_keys($this->purgeComputedProperties($row)));
1319  if ($wsAlt = $this->getWorkspaceVersionOfRecord($this->versioningWorkspaceId, $table, $row['uid'], $fieldNames, $bypassEnableFieldsCheck)) {
1320  if (is_array($wsAlt)) {
1321  // Always fix PID (like in fixVersioningPid() above). [This is usually not
1322  // the important factor for versioning OL]
1323  // Keep the old (-1) - indicates it was a version...
1324  $wsAlt['_ORIG_pid'] = $wsAlt['pid'];
1325  // Set in the online versions PID.
1326  $wsAlt['pid'] = $row['pid'];
1327  // For versions of single elements or page+content, preserve online UID and PID
1328  // (this will produce true "overlay" of element _content_, not any references)
1329  // For page+content the "_ORIG_uid" should actually be used as PID for selection
1330  // of tables with "versioning_followPages" enabled.
1331  $wsAlt['_ORIG_uid'] = $wsAlt['uid'];
1332  $wsAlt['uid'] = $row['uid'];
1333  // Translate page alias as well so links are pointing to the _online_ page:
1334  if ($table === 'pages') {
1335  $wsAlt['alias'] = $row['alias'];
1336  }
1337  // Changing input record to the workspace version alternative:
1338  $row = $wsAlt;
1339  // Check if it is deleted/new
1340  $rowVersionState = VersionState::cast($row['t3ver_state']);
1341  if (
1342  $rowVersionState->equals(VersionState::NEW_PLACEHOLDER)
1343  || $rowVersionState->equals(VersionState::DELETE_PLACEHOLDER)
1344  ) {
1345  // Unset record if it turned out to be deleted in workspace
1346  $row = false;
1347  }
1348  // Check if move-pointer in workspace (unless if a move-placeholder is the
1349  // reason why it appears!):
1350  // You have to specifically set $unsetMovePointers in order to clear these
1351  // because it is normally a display issue if it should be shown or not.
1352  if (
1353  ($rowVersionState->equals(VersionState::MOVE_POINTER)
1354  && !$movePldSwap
1355  ) && $unsetMovePointers
1356  ) {
1357  // Unset record if it turned out to be deleted in workspace
1358  $row = false;
1359  }
1360  } else {
1361  // No version found, then check if t3ver_state = VersionState::NEW_PLACEHOLDER
1362  // (online version is dummy-representation)
1363  // Notice, that unless $bypassEnableFieldsCheck is TRUE, the $row is unset if
1364  // enablefields for BOTH the version AND the online record deselects it. See
1365  // note for $bypassEnableFieldsCheck
1367  $versionState = VersionState::cast($row['t3ver_state']);
1368  if ($wsAlt <= -1 || $versionState->indicatesPlaceholder()) {
1369  // Unset record if it turned out to be "hidden"
1370  $row = false;
1371  }
1372  }
1373  }
1374  }
1375  }
1376 
1387  public function movePlhOL($table, &$row)
1388  {
1389  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
1390  && (int)VersionState::cast($row['t3ver_state'])->equals(VersionState::MOVE_PLACEHOLDER)
1391  ) {
1392  // Only for WS ver 2... (moving) - enabled by default with CMS7
1393  // If t3ver_move_id is not found, then find it (but we like best if it is here)
1394  if (!isset($row['t3ver_move_id'])) {
1395  $moveIDRec = $this->getRawRecord($table, $row['uid'], 't3ver_move_id', true);
1396  $moveID = $moveIDRec['t3ver_move_id'];
1397  } else {
1398  $moveID = $row['t3ver_move_id'];
1399  }
1400  // Find pointed-to record.
1401  if ($moveID) {
1402  $res = $this->getDatabaseConnection()->exec_SELECTquery(implode(',', array_keys($row)), $table, 'uid=' . (int)$moveID . $this->enableFields($table));
1403  $origRow = $this->getDatabaseConnection()->sql_fetch_assoc($res);
1404  $this->getDatabaseConnection()->sql_free_result($res);
1405  if ($origRow) {
1406  $row = $origRow;
1407  return true;
1408  }
1409  }
1410  }
1411  return false;
1412  }
1413 
1423  public function getMovePlaceholder($table, $uid, $fields = '*')
1424  {
1425  if ($this->versioningPreview) {
1426  $workspace = (int)$this->versioningWorkspaceId;
1427  if (!empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) && $workspace !== 0) {
1428  // Select workspace version of record:
1429  $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, 'pid<>-1 AND
1430  t3ver_state=' . new VersionState(VersionState::MOVE_PLACEHOLDER) . ' AND
1431  t3ver_move_id=' . (int)$uid . ' AND
1432  t3ver_wsid=' . (int)$workspace . $this->deleteClause($table));
1433  if (is_array($row)) {
1434  return $row;
1435  }
1436  }
1437  }
1438  return false;
1439  }
1440 
1452  public function getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields = '*', $bypassEnableFieldsCheck = false)
1453  {
1454  if ($workspace !== 0 && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
1455  $workspace = (int)$workspace;
1456  $uid = (int)$uid;
1457  // Setting up enableFields for version record
1458  $enFields = $this->enableFields($table, -1, array(), true);
1459  // Select workspace version of record, only testing for deleted.
1460  $newrow = $this->getDatabaseConnection()->exec_SELECTgetSingleRow($fields, $table, 'pid=-1 AND
1461  t3ver_oid=' . $uid . ' AND
1462  t3ver_wsid=' . $workspace . $this->deleteClause($table));
1463  // If version found, check if it could have been selected with enableFields on
1464  // as well:
1465  if (is_array($newrow)) {
1466  if ($bypassEnableFieldsCheck || $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', $table, 'pid=-1 AND
1467  t3ver_oid=' . $uid . ' AND
1468  t3ver_wsid=' . $workspace . $enFields)) {
1469  // Return offline version, tested for its enableFields.
1470  return $newrow;
1471  } else {
1472  // Return -1 because offline version was de-selected due to its enableFields.
1473  return -1;
1474  }
1475  } else {
1476  // OK, so no workspace version was found. Then check if online version can be
1477  // selected with full enable fields and if so, return 1:
1478  if ($bypassEnableFieldsCheck || $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', $table, 'uid=' . $uid . $enFields)) {
1479  // Means search was done, but no version found.
1480  return 1;
1481  } else {
1482  // Return -2 because the online record was de-selected due to its enableFields.
1483  return -2;
1484  }
1485  }
1486  }
1487  // No look up in database because versioning not enabled / or workspace not
1488  // offline
1489  return false;
1490  }
1491 
1498  public function checkWorkspaceAccess($wsid)
1499  {
1500  if (!$this->getBackendUser() || !ExtensionManagementUtility::isLoaded('workspaces')) {
1501  return false;
1502  }
1503  if (isset($this->workspaceCache[$wsid])) {
1504  $ws = $this->workspaceCache[$wsid];
1505  } else {
1506  if ($wsid > 0) {
1507  // No $GLOBALS['TCA'] yet!
1508  $ws = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('*', 'sys_workspace', 'uid=' . (int)$wsid . ' AND deleted=0');
1509  if (!is_array($ws)) {
1510  return false;
1511  }
1512  } else {
1513  $ws = $wsid;
1514  }
1515  $ws = $this->getBackendUser()->checkWorkspace($ws);
1516  $this->workspaceCache[$wsid] = $ws;
1517  }
1518  return (string)$ws['_ACCESS'] !== '';
1519  }
1520 
1529  public function getFileReferences($tableName, $fieldName, array $element)
1530  {
1532  $fileRepository = GeneralUtility::makeInstance(FileRepository::class);
1533  $currentId = !empty($element['uid']) ? $element['uid'] : 0;
1534 
1535  // Fetch the references of the default element
1536  try {
1537  $references = $fileRepository->findByRelation($tableName, $fieldName, $currentId);
1538  } catch (FileDoesNotExistException $e) {
1543  return array();
1544  } catch (\InvalidArgumentException $e) {
1549  $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')';
1550  GeneralUtility::sysLog($logMessage, 'core', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1551  return array();
1552  }
1553 
1554  $localizedId = null;
1555  if (isset($element['_LOCALIZED_UID'])) {
1556  $localizedId = $element['_LOCALIZED_UID'];
1557  } elseif (isset($element['_PAGES_OVERLAY_UID'])) {
1558  $localizedId = $element['_PAGES_OVERLAY_UID'];
1559  }
1560 
1561  if (!empty($GLOBALS['TCA'][$tableName]['ctrl']['transForeignTable'])) {
1562  $tableName = $GLOBALS['TCA'][$tableName]['ctrl']['transForeignTable'];
1563  }
1564 
1565  $isTableLocalizable = (
1566  !empty($GLOBALS['TCA'][$tableName]['ctrl']['languageField'])
1567  && !empty($GLOBALS['TCA'][$tableName]['ctrl']['transOrigPointerField'])
1568  );
1569  if ($isTableLocalizable && $localizedId !== null) {
1570  $localizedReferences = $fileRepository->findByRelation($tableName, $fieldName, $localizedId);
1571  $localizedReferencesValue = $localizedReferences ?: '';
1572  if ($this->shouldFieldBeOverlaid($tableName, $fieldName, $localizedReferencesValue)) {
1573  $references = $localizedReferences;
1574  }
1575  }
1576 
1577  return $references;
1578  }
1579 
1587  protected function purgeComputedProperties(array $row)
1588  {
1589  foreach ($this->computedPropertyNames as $computedPropertyName) {
1590  if (array_key_exists($computedPropertyName, $row)) {
1591  unset($row[$computedPropertyName]);
1592  }
1593  }
1594  return $row;
1595  }
1596 
1605  protected function shouldFieldBeOverlaid($table, $field, $value)
1606  {
1607  $l10n_mode = isset($GLOBALS['TCA'][$table]['columns'][$field]['l10n_mode'])
1608  ? $GLOBALS['TCA'][$table]['columns'][$field]['l10n_mode']
1609  : '';
1610 
1611  $shouldFieldBeOverlaid = true;
1612 
1613  if ($l10n_mode === 'exclude') {
1614  $shouldFieldBeOverlaid = false;
1615  } elseif ($l10n_mode === 'mergeIfNotBlank') {
1616  $checkValue = $value;
1617 
1618  // 0 values are considered blank when coming from a group field
1619  if (empty($value) && $GLOBALS['TCA'][$table]['columns'][$field]['config']['type'] === 'group') {
1620  $checkValue = '';
1621  }
1622 
1623  if ($checkValue === array() || !is_array($checkValue) && trim($checkValue) === '') {
1624  $shouldFieldBeOverlaid = false;
1625  }
1626  }
1627 
1628  return $shouldFieldBeOverlaid;
1629  }
1630 
1636  protected function getDatabaseConnection()
1637  {
1638  return $GLOBALS['TYPO3_DB'];
1639  }
1640 
1644  protected function getTypoScriptFrontendController()
1645  {
1646  return $GLOBALS['TSFE'];
1647  }
1648 
1654  protected function getBackendUser()
1655  {
1656  return $GLOBALS['BE_USER'];
1657  }
1658 }