TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
TypoScriptFrontendController.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Frontend\Controller;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
17 use Doctrine\DBAL\Exception\ConnectionException;
54 
71 {
76  public $id = '';
77 
82  public $type = '';
83 
88  public $cHash = '';
89 
95  public $no_cache = false;
96 
101  public $rootLine = '';
102 
107  public $page = '';
108 
114  public $contentPid = 0;
115 
123  protected $originalMountPointPage = null;
124 
133  protected $originalShortcutPage = null;
134 
140  public $sys_page = '';
141 
150  protected $activeUrlHandlers = [];
151 
156  public $pageNotFound = 0;
157 
162  public $domainStartPage = 0;
163 
169 
173  public $MP = '';
174 
178  public $RDCT = '';
179 
186  public $page_cache_reg1 = 0;
187 
194  public $siteScript = '';
195 
201  public $fe_user = '';
202 
209  public $loginUser = false;
210 
217  public $gr_list = '';
218 
223  public $beUserLogin = false;
224 
229  public $workspacePreview = 0;
230 
235  public $loginAllowedInBranch = true;
236 
242 
248 
256  public $fePreview = 0;
257 
263  public $showHiddenPage = false;
264 
271  public $showHiddenRecords = false;
272 
277  public $simUserGroup = 0;
278 
284  public $config = '';
285 
291  public $tmpl = null;
292 
298  public $cacheTimeOutDefault = false;
299 
305  public $cacheContentFlag = false;
306 
311  public $cacheExpires = 0;
312 
317  public $isClientCachable = false;
318 
325  public $all = [];
326 
331  public $sPre = '';
332 
338  public $pSetup = '';
339 
345  public $newHash = '';
346 
354  public $getMethodUrlIdToken = '';
355 
363  public $no_cacheBeforePageGen = false;
364 
370  public $tempContent = false;
371 
376  public $forceTemplateParsing = false;
377 
382  public $cHash_array = [];
383 
388  public $pagesTSconfig = '';
389 
408 
414 
422 
428  public $additionalCSS = [];
429 
433  public $JSCode;
434 
438  public $inlineJS;
439 
444  public $divSection = '';
445 
450  public $debug = false;
451 
456  public $intTarget = '';
457 
462  public $extTarget = '';
463 
468  public $fileTarget = '';
469 
475  public $MP_defaults = [];
476 
482 
487  public $absRefPrefix = '';
488 
494 
499  public $lockFilePath = '';
500 
505  public $ATagParams = '';
506 
513  public $sWordRegEx = '';
514 
520  public $sWordList = '';
521 
528  public $linkVars = '';
529 
536  public $excludeCHashVars = '';
537 
543  public $displayEditIcons = '';
544 
552 
560  public $sys_language_uid = 0;
561 
566  public $sys_language_mode = '';
567 
574 
582 
589 
595  public $applicationData = [];
596 
600  public $register = [];
601 
607  public $registerStack = [];
608 
614  public $cObjectDepthCounter = 50;
615 
621  public $recordRegister = [];
622 
630  public $currentRecord = '';
631 
637  public $accessKey = [];
638 
644  public $imagesOnPage = [];
645 
651  public $lastImageInfo = [];
652 
658  public $uniqueCounter = 0;
659 
663  public $uniqueString = '';
664 
670  public $indexedDocTitle = '';
671 
677  public $altPageTitle = '';
678 
683  public $baseUrl = '';
684 
689  private $usedUniqueIds = [];
690 
696  public $cObj = '';
697 
702  public $content = '';
703 
707  public $scriptParseTime = 0;
708 
716  public $csConvObj;
717 
723  public $defaultCharSet = 'utf-8';
724 
730  public $renderCharset = 'utf-8';
731 
738  public $metaCharset = 'utf-8';
739 
744  public $lang = '';
745 
749  public $LL_labels_cache = [];
750 
754  public $LL_files_cache = [];
755 
763  protected $languageDependencies = [];
764 
768  protected $locks = [];
769 
773  protected $pageRenderer = null;
774 
781  protected $pageCache;
782 
786  protected $pageCacheTags = [];
787 
793  protected $cacheHash;
794 
800  protected $domainDataCache = [];
801 
809  protected $contentType = 'text/html';
810 
818  public $xhtmlDoctype = '';
819 
824 
830  protected $requestedId;
831 
836 
853  public function __construct($_ = null, $id, $type, $no_cache = '', $cHash = '', $_2 = null, $MP = '', $RDCT = '')
854  {
855  // Setting some variables:
856  $this->id = $id;
857  $this->type = $type;
858  if ($no_cache) {
859  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) {
860  $warning = '&no_cache=1 has been ignored because $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set!';
861  $this->getTimeTracker()->setTSlogMessage($warning, 2);
862  } else {
863  $warning = '&no_cache=1 has been supplied, so caching is disabled! URL: "' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '"';
864  $this->disableCache();
865  }
866  GeneralUtility::sysLog($warning, 'cms', GeneralUtility::SYSLOG_SEVERITY_WARNING);
867  }
868  $this->cHash = $cHash;
869  $this->MP = $GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids'] ? (string)$MP : '';
870  $this->RDCT = $RDCT;
871  $this->uniqueString = md5(microtime());
872  $this->csConvObj = GeneralUtility::makeInstance(CharsetConverter::class);
873  $this->initPageRenderer();
874  // Call post processing function for constructor:
875  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'])) {
876  $_params = ['pObj' => &$this];
877  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'] as $_funcRef) {
878  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
879  }
880  }
881  $this->cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
882  $this->initCaches();
883  }
884 
888  protected function initPageRenderer()
889  {
890  if ($this->pageRenderer !== null) {
891  return;
892  }
893  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
894  $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
895  }
896 
901  public function setContentType($contentType)
902  {
903  $this->contentType = $contentType;
904  }
905 
914  public function connectToDB()
915  {
916  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages');
917  try {
918  $connection->connect();
919  } catch (ConnectionException $exception) {
920  // Cannot connect to current database
921  $message = 'Cannot connect to the configured database "' . $connection->getDatabase() . '"';
922  if ($this->checkPageUnavailableHandler()) {
923  $this->pageUnavailableAndExit($message);
924  } else {
925  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
926  throw new ServiceUnavailableException($message, 1301648782);
927  }
928  }
929  // Call post processing function for DB connection:
930  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'])) {
931  $_params = ['pObj' => &$this];
932  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] as $_funcRef) {
933  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
934  }
935  }
936  }
937 
946  public function sendRedirect()
947  {
948  if ($this->RDCT) {
949  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
950  ->getQueryBuilderForTable('cache_md5params');
951 
952  $row = $queryBuilder
953  ->select('params')
954  ->from('cache_md5params')
955  ->where(
956  $queryBuilder->expr()->eq(
957  'md5hash',
958  $queryBuilder->createNamedParameter($this->RDCT, \PDO::PARAM_STR)
959  )
960  )
961  ->execute()
962  ->fetch();
963 
964  if ($row) {
965  $this->updateMD5paramsRecord($this->RDCT);
966  header('Location: ' . $row['params']);
967  die;
968  }
969  }
970  }
971 
972  /********************************************
973  *
974  * Initializing, resolving page id
975  *
976  ********************************************/
982  protected function initCaches()
983  {
984  $this->pageCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pages');
985  }
986 
992  public function initFEuser()
993  {
994  $this->fe_user = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
995  // List of pid's acceptable
996  $pid = GeneralUtility::_GP('pid');
997  $this->fe_user->checkPid_value = $pid ? implode(',', GeneralUtility::intExplode(',', $pid)) : 0;
998  // Check if a session is transferred:
999  if (GeneralUtility::_GP('FE_SESSION_KEY')) {
1000  $fe_sParts = explode('-', GeneralUtility::_GP('FE_SESSION_KEY'));
1001  // If the session key hash check is OK:
1002  if (md5(($fe_sParts[0] . '/' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey'])) === (string)$fe_sParts[1]) {
1004  $_COOKIE[$cookieName] = $fe_sParts[0];
1005  if (isset($_SERVER['HTTP_COOKIE'])) {
1006  // See http://forge.typo3.org/issues/27740
1007  $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $fe_sParts[0];
1008  }
1009  $this->fe_user->forceSetCookie = 1;
1010  $this->fe_user->dontSetCookie = false;
1011  unset($cookieName);
1012  }
1013  }
1014  $this->fe_user->start();
1015  $this->fe_user->unpack_uc();
1016  // Gets session data
1017  $this->fe_user->fetchSessionData();
1018  $recs = GeneralUtility::_GP('recs');
1019  // If any record registration is submitted, register the record.
1020  if (is_array($recs)) {
1021  $this->fe_user->record_registration($recs, $GLOBALS['TYPO3_CONF_VARS']['FE']['maxSessionDataSize']);
1022  }
1023  // Call hook for possible manipulation of frontend user object
1024  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'])) {
1025  $_params = ['pObj' => &$this];
1026  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] as $_funcRef) {
1027  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1028  }
1029  }
1030  // For every 60 seconds the is_online timestamp is updated.
1031  if (is_array($this->fe_user->user) && $this->fe_user->user['uid'] && $this->fe_user->user['is_online'] < $GLOBALS['EXEC_TIME'] - 60) {
1032  $dbConnection = GeneralUtility::makeInstance(ConnectionPool::class)
1033  ->getConnectionForTable('fe_users');
1034  $dbConnection->update(
1035  'fe_users',
1036  [
1037  'is_online' => $GLOBALS['EXEC_TIME']
1038  ],
1039  [
1040  'uid' => (int)$this->fe_user->user['uid']
1041  ]
1042  );
1043  }
1044  }
1045 
1052  public function initUserGroups()
1053  {
1054  // This affects the hidden-flag selecting the fe_groups for the user!
1055  $this->fe_user->showHiddenRecords = $this->showHiddenRecords;
1056  // no matter if we have an active user we try to fetch matching groups which can be set without an user (simulation for instance!)
1057  $this->fe_user->fetchGroupData();
1058  if (is_array($this->fe_user->user) && !empty($this->fe_user->groupData['uid'])) {
1059  // global flag!
1060  $this->loginUser = true;
1061  // group -2 is not an existing group, but denotes a 'default' group when a user IS logged in. This is used to let elements be shown for all logged in users!
1062  $this->gr_list = '0,-2';
1063  $gr_array = $this->fe_user->groupData['uid'];
1064  } else {
1065  $this->loginUser = false;
1066  // group -1 is not an existing group, but denotes a 'default' group when not logged in. This is used to let elements be hidden, when a user is logged in!
1067  $this->gr_list = '0,-1';
1068  if ($this->loginAllowedInBranch) {
1069  // For cases where logins are not banned from a branch usergroups can be set based on IP masks so we should add the usergroups uids.
1070  $gr_array = $this->fe_user->groupData['uid'];
1071  } else {
1072  // Set to blank since we will NOT risk any groups being set when no logins are allowed!
1073  $gr_array = [];
1074  }
1075  }
1076  // Clean up.
1077  // Make unique...
1078  $gr_array = array_unique($gr_array);
1079  // sort
1080  sort($gr_array);
1081  if (!empty($gr_array) && !$this->loginAllowedInBranch_mode) {
1082  $this->gr_list .= ',' . implode(',', $gr_array);
1083  }
1084  if ($this->fe_user->writeDevLog) {
1085  GeneralUtility::devLog('Valid usergroups for TSFE: ' . $this->gr_list, __CLASS__);
1086  }
1087  }
1088 
1094  public function isUserOrGroupSet()
1095  {
1096  return is_array($this->fe_user->user) || $this->gr_list !== '0,-1';
1097  }
1098 
1108  public function checkAlternativeIdMethods()
1109  {
1110  $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT');
1111  // Call post processing function for custom URL methods.
1112  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'])) {
1113  $_params = ['pObj' => &$this];
1114  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] as $_funcRef) {
1115  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1116  }
1117  }
1118  }
1119 
1127  public function clear_preview()
1128  {
1129  $this->showHiddenPage = false;
1130  $this->showHiddenRecords = false;
1131  $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
1132  $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
1133  $this->fePreview = 0;
1134  }
1135 
1141  public function isBackendUserLoggedIn()
1142  {
1143  return (bool)$this->beUserLogin;
1144  }
1145 
1151  public function initializeBackendUser()
1152  {
1153  // PRE BE_USER HOOK
1154  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'])) {
1155  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] as $_funcRef) {
1156  $_params = [];
1157  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1158  }
1159  }
1161  $BE_USER = null;
1162  // If the backend cookie is set,
1163  // we proceed and check if a backend user is logged in.
1164  if ($_COOKIE[BackendUserAuthentication::getCookieName()]) {
1165  $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] = microtime(true);
1166  $this->getTimeTracker()->push('Back End user initialized', '');
1167  // @todo validate the comment below: is this necessary? if so,
1168  // formfield_status should be set to "" in \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
1169  // which is a subclass of \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1170  // ----
1171  // the value this->formfield_status is set to empty in order to
1172  // disable login-attempts to the backend account through this script
1173  // New backend user object
1174  $BE_USER = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class);
1175  // Object is initialized
1176  $BE_USER->start();
1177  $BE_USER->unpack_uc();
1178  if (!empty($BE_USER->user['uid'])) {
1179  $BE_USER->fetchGroupData();
1180  $this->beUserLogin = true;
1181  }
1182  // Unset the user initialization.
1183  if (!$BE_USER->checkLockToIP() || !$BE_USER->checkBackendAccessSettingsFromInitPhp() || empty($BE_USER->user['uid'])) {
1184  $BE_USER = null;
1185  $this->beUserLogin = false;
1186  }
1187  $this->getTimeTracker()->pull();
1188  $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] = microtime(true);
1189  }
1190  // POST BE_USER HOOK
1191  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'])) {
1192  $_params = [
1193  'BE_USER' => &$BE_USER
1194  ];
1195  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'] as $_funcRef) {
1196  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1197  }
1198  }
1199  return $BE_USER;
1200  }
1201 
1210  public function determineId()
1211  {
1212  // Call pre processing function for id determination
1213  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'])) {
1214  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'] as $functionReference) {
1215  $parameters = ['parentObject' => $this];
1216  GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1217  }
1218  }
1219  // If there is a Backend login we are going to check for any preview settings:
1220  $this->getTimeTracker()->push('beUserLogin', '');
1221  $originalFrontendUser = null;
1222  $backendUser = $this->getBackendUser();
1223  if ($this->beUserLogin || $this->doWorkspacePreview()) {
1224  // Backend user preview features:
1225  if ($this->beUserLogin && $backendUser->adminPanel instanceof AdminPanelView) {
1226  $this->fePreview = (int)$backendUser->adminPanel->extGetFeAdminValue('preview');
1227  // If admin panel preview is enabled...
1228  if ($this->fePreview) {
1229  if ($this->fe_user->user) {
1230  $originalFrontendUser = $this->fe_user->user;
1231  }
1232  $this->showHiddenPage = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenPages');
1233  $this->showHiddenRecords = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenRecords');
1234  // Simulate date
1235  $simTime = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateDate');
1236  if ($simTime) {
1237  $GLOBALS['SIM_EXEC_TIME'] = $simTime;
1238  $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
1239  }
1240  // simulate user
1241  $simUserGroup = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateUserGroup');
1242  $this->simUserGroup = $simUserGroup;
1243  if ($simUserGroup) {
1244  if ($this->fe_user->user) {
1245  $this->fe_user->user[$this->fe_user->usergroup_column] = $simUserGroup;
1246  } else {
1247  $this->fe_user->user = [
1248  $this->fe_user->usergroup_column => $simUserGroup
1249  ];
1250  }
1251  }
1252  if (!$simUserGroup && !$simTime && !$this->showHiddenPage && !$this->showHiddenRecords) {
1253  $this->fePreview = 0;
1254  }
1255  }
1256  }
1257  if ($this->id) {
1258  if ($this->determineIdIsHiddenPage()) {
1259  // The preview flag is set only if the current page turns out to actually be hidden!
1260  $this->fePreview = 1;
1261  $this->showHiddenPage = true;
1262  }
1263  // For Live workspace: Check root line for proper connection to tree root (done because of possible preview of page / branch versions)
1264  if (!$this->fePreview && $this->whichWorkspace() === 0) {
1265  // Initialize the page-select functions to check rootline:
1266  $temp_sys_page = GeneralUtility::makeInstance(PageRepository::class);
1267  $temp_sys_page->init($this->showHiddenPage);
1268  // If root line contained NO records and ->error_getRootLine_failPid tells us that it was because of a pid=-1 (indicating a "version" record)...:
1269  if (empty($temp_sys_page->getRootLine($this->id, $this->MP)) && $temp_sys_page->error_getRootLine_failPid == -1) {
1270  // Setting versioningPreview flag and try again:
1271  $temp_sys_page->versioningPreview = true;
1272  if (!empty($temp_sys_page->getRootLine($this->id, $this->MP))) {
1273  // Finally, we got a root line (meaning that it WAS due to versioning preview of a page somewhere) and we set the fePreview flag which in itself will allow sys_page class to display previews of versionized records.
1274  $this->fePreview = 1;
1275  }
1276  }
1277  }
1278  }
1279  // The preview flag will be set if a backend user is in an offline workspace
1280  if (
1281  (
1282  $backendUser->user['workspace_preview']
1283  || GeneralUtility::_GP('ADMCMD_view')
1284  || $this->doWorkspacePreview()
1285  )
1286  && (
1287  $this->whichWorkspace() === -1
1288  || $this->whichWorkspace() > 0
1289  )
1290  && !GeneralUtility::_GP('ADMCMD_noBeUser')
1291  ) {
1292  // Will show special preview message.
1293  $this->fePreview = 2;
1294  }
1295  // If the front-end is showing a preview, caching MUST be disabled.
1296  if ($this->fePreview) {
1297  $this->disableCache();
1298  }
1299  }
1300  $this->getTimeTracker()->pull();
1301  // Now, get the id, validate access etc:
1302  $this->fetch_the_id();
1303  // Check if backend user has read access to this page. If not, recalculate the id.
1304  if ($this->beUserLogin && $this->fePreview) {
1305  if (!$backendUser->doesUserHaveAccess($this->page, 1)) {
1306  // Resetting
1307  $this->clear_preview();
1308  $this->fe_user->user = $originalFrontendUser;
1309  // Fetching the id again, now with the preview settings reset.
1310  $this->fetch_the_id();
1311  }
1312  }
1313  // Checks if user logins are blocked for a certain branch and if so, will unset user login and re-fetch ID.
1314  $this->loginAllowedInBranch = $this->checkIfLoginAllowedInBranch();
1315  // Logins are not allowed:
1316  if (!$this->loginAllowedInBranch) {
1317  // Only if there is a login will we run this...
1318  if ($this->isUserOrGroupSet()) {
1319  if ($this->loginAllowedInBranch_mode == 'all') {
1320  // Clear out user and group:
1321  $this->fe_user->hideActiveLogin();
1322  $this->gr_list = '0,-1';
1323  } else {
1324  $this->gr_list = '0,-2';
1325  }
1326  // Fetching the id again, now with the preview settings reset.
1327  $this->fetch_the_id();
1328  }
1329  }
1330  // Final cleaning.
1331  // Make sure it's an integer
1332  $this->id = ($this->contentPid = (int)$this->id);
1333  // Make sure it's an integer
1334  $this->type = (int)$this->type;
1335  // Call post processing function for id determination:
1336  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'])) {
1337  $_params = ['pObj' => &$this];
1338  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'] as $_funcRef) {
1339  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1340  }
1341  }
1342  }
1343 
1350  protected function determineIdIsHiddenPage()
1351  {
1352  $field = MathUtility::canBeInterpretedAsInteger($this->id) ? 'uid' : 'alias';
1353 
1354  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1355  ->getQueryBuilderForTable('pages');
1356  $queryBuilder
1357  ->getRestrictions()
1358  ->removeAll()
1359  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1360 
1361  $page = $queryBuilder
1362  ->select('uid', 'hidden', 'starttime', 'endtime')
1363  ->from('pages')
1364  ->where(
1365  $queryBuilder->expr()->eq($field, $queryBuilder->createNamedParameter($this->id)),
1366  $queryBuilder->expr()->gte('pid', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT))
1367  )
1368  ->setMaxResults(1)
1369  ->execute()
1370  ->fetch();
1371 
1372  $workspace = $this->whichWorkspace();
1373  if ($workspace !== 0 && $workspace !== false) {
1374  // Fetch overlay of page if in workspace and check if it is hidden
1375  $pageSelectObject = GeneralUtility::makeInstance(PageRepository::class);
1376  $pageSelectObject->versioningPreview = true;
1377  $pageSelectObject->init(false);
1378  $targetPage = $pageSelectObject->getWorkspaceVersionOfRecord($this->whichWorkspace(), 'pages', $page['uid']);
1379  $result = $targetPage === -1 || $targetPage === -2;
1380  } else {
1381  $result = is_array($page) && ($page['hidden'] || $page['starttime'] > $GLOBALS['SIM_EXEC_TIME'] || $page['endtime'] != 0 && $page['endtime'] <= $GLOBALS['SIM_EXEC_TIME']);
1382  }
1383  return $result;
1384  }
1385 
1395  public function fetch_the_id()
1396  {
1397  $timeTracker = $this->getTimeTracker();
1398  $timeTracker->push('fetch_the_id initialize/', '');
1399  // Initialize the page-select functions.
1400  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class);
1401  $this->sys_page->versioningPreview = $this->fePreview === 2 || (int)$this->workspacePreview || (bool)GeneralUtility::_GP('ADMCMD_view');
1402  $this->sys_page->versioningWorkspaceId = $this->whichWorkspace();
1403  $this->sys_page->init($this->showHiddenPage);
1404  // Set the valid usergroups for FE
1405  $this->initUserGroups();
1406  // Sets sys_page where-clause
1407  $this->setSysPageWhereClause();
1408  // Splitting $this->id by a period (.).
1409  // First part is 'id' and second part (if exists) will overrule the &type param
1410  $idParts = explode('.', $this->id, 2);
1411  $this->id = $idParts[0];
1412  if (isset($idParts[1])) {
1413  $this->type = $idParts[1];
1414  }
1415 
1416  // If $this->id is a string, it's an alias
1417  $this->checkAndSetAlias();
1418  // The id and type is set to the integer-value - just to be sure...
1419  $this->id = (int)$this->id;
1420  $this->type = (int)$this->type;
1421  $timeTracker->pull();
1422  // We find the first page belonging to the current domain
1423  $timeTracker->push('fetch_the_id domain/', '');
1424  // The page_id of the current domain
1425  $this->domainStartPage = $this->findDomainRecord($GLOBALS['TYPO3_CONF_VARS']['SYS']['recursiveDomainSearch']);
1426  if (!$this->id) {
1427  if ($this->domainStartPage) {
1428  // If the id was not previously set, set it to the id of the domain.
1429  $this->id = $this->domainStartPage;
1430  } else {
1431  // Find the first 'visible' page in that domain
1432  $theFirstPage = $this->sys_page->getFirstWebPage($this->id);
1433  if ($theFirstPage) {
1434  $this->id = $theFirstPage['uid'];
1435  } else {
1436  $message = 'No pages are found on the rootlevel!';
1437  if ($this->checkPageUnavailableHandler()) {
1438  $this->pageUnavailableAndExit($message);
1439  } else {
1440  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1441  throw new ServiceUnavailableException($message, 1301648975);
1442  }
1443  }
1444  }
1445  }
1446  $timeTracker->pull();
1447  $timeTracker->push('fetch_the_id rootLine/', '');
1448  // We store the originally requested id
1449  $this->requestedId = $this->id;
1450  $this->getPageAndRootlineWithDomain($this->domainStartPage);
1451  $timeTracker->pull();
1452  if ($this->pageNotFound && $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling']) {
1453  $pNotFoundMsg = [
1454  1 => 'ID was not an accessible page',
1455  2 => 'Subsection was found and not accessible',
1456  3 => 'ID was outside the domain',
1457  4 => 'The requested page alias does not exist'
1458  ];
1459  $this->pageNotFoundAndExit($pNotFoundMsg[$this->pageNotFound]);
1460  }
1461  if ($this->page['url_scheme'] > 0) {
1462  $newUrl = '';
1463  $requestUrlScheme = parse_url(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
1464  if ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTP && $requestUrlScheme == 'https') {
1465  $newUrl = 'http://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 8);
1466  } elseif ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTPS && $requestUrlScheme == 'http') {
1467  $newUrl = 'https://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 7);
1468  }
1469  if ($newUrl !== '') {
1470  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
1471  $headerCode = HttpUtility::HTTP_STATUS_303;
1472  } else {
1473  $headerCode = HttpUtility::HTTP_STATUS_301;
1474  }
1475  HttpUtility::redirect($newUrl, $headerCode);
1476  }
1477  }
1478  // Set no_cache if set
1479  if ($this->page['no_cache']) {
1480  $this->set_no_cache('no_cache is set in page properties');
1481  }
1482  // Init SYS_LASTCHANGED
1483  $this->register['SYS_LASTCHANGED'] = (int)$this->page['tstamp'];
1484  if ($this->register['SYS_LASTCHANGED'] < (int)$this->page['SYS_LASTCHANGED']) {
1485  $this->register['SYS_LASTCHANGED'] = (int)$this->page['SYS_LASTCHANGED'];
1486  }
1487  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'])) {
1488  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'] as $functionReference) {
1489  $parameters = ['parentObject' => $this];
1490  GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1491  }
1492  }
1493  }
1494 
1510  public function getPageAndRootline()
1511  {
1512  $this->page = $this->sys_page->getPage($this->id);
1513  if (empty($this->page)) {
1514  // If no page, we try to find the page before in the rootLine.
1515  // Page is 'not found' in case the id itself was not an accessible page. code 1
1516  $this->pageNotFound = 1;
1517  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1518  if (!empty($this->rootLine)) {
1519  $c = count($this->rootLine) - 1;
1520  while ($c > 0) {
1521  // Add to page access failure history:
1522  $this->pageAccessFailureHistory['direct_access'][] = $this->rootLine[$c];
1523  // Decrease to next page in rootline and check the access to that, if OK, set as page record and ID value.
1524  $c--;
1525  $this->id = $this->rootLine[$c]['uid'];
1526  $this->page = $this->sys_page->getPage($this->id);
1527  if (!empty($this->page)) {
1528  break;
1529  }
1530  }
1531  }
1532  // If still no page...
1533  if (empty($this->page)) {
1534  $message = 'The requested page does not exist!';
1535  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling']) {
1536  $this->pageNotFoundAndExit($message);
1537  } else {
1538  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1539  throw new PageNotFoundException($message, 1301648780);
1540  }
1541  }
1542  }
1543  // Spacer is not accessible in frontend
1544  if ($this->page['doktype'] == PageRepository::DOKTYPE_SPACER) {
1545  $message = 'The requested page does not exist!';
1546  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling']) {
1547  $this->pageNotFoundAndExit($message);
1548  } else {
1549  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1550  throw new PageNotFoundException($message, 1301648781);
1551  }
1552  }
1553  // Is the ID a link to another page??
1554  if ($this->page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1555  // We need to clear MP if the page is a shortcut. Reason is if the short cut goes to another page, then we LEAVE the rootline which the MP expects.
1556  $this->MP = '';
1557  // saving the page so that we can check later - when we know
1558  // about languages - whether we took the correct shortcut or
1559  // whether a translation of the page overwrites the shortcut
1560  // target and we need to follow the new target
1561  $this->originalShortcutPage = $this->page;
1562  $this->page = $this->getPageShortcut($this->page['shortcut'], $this->page['shortcut_mode'], $this->page['uid']);
1563  $this->id = $this->page['uid'];
1564  }
1565  // If the page is a mountpoint which should be overlaid with the contents of the mounted page,
1566  // it must never be accessible directly, but only in the mountpoint context. Therefore we change
1567  // the current ID and the user is redirected by checkPageForMountpointRedirect().
1568  if ($this->page['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT && $this->page['mount_pid_ol']) {
1569  $this->originalMountPointPage = $this->page;
1570  $this->page = $this->sys_page->getPage($this->page['mount_pid']);
1571  if (empty($this->page)) {
1572  $message = 'This page (ID ' . $this->originalMountPointPage['uid'] . ') is of type "Mount point" and '
1573  . 'mounts a page which is not accessible (ID ' . $this->originalMountPointPage['mount_pid'] . ').';
1574  throw new PageNotFoundException($message, 1402043263);
1575  }
1576  $this->MP = $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
1577  $this->id = $this->page['uid'];
1578  }
1579  // Gets the rootLine
1580  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1581  // If not rootline we're off...
1582  if (empty($this->rootLine)) {
1583  $ws = $this->whichWorkspace();
1584  if ($this->sys_page->error_getRootLine_failPid == -1 && $ws) {
1585  $this->sys_page->versioningPreview = true;
1586  $this->sys_page->versioningWorkspaceId = $ws;
1587  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1588  }
1589  if (empty($this->rootLine)) {
1590  $message = 'The requested page didn\'t have a proper connection to the tree-root!';
1591  if ($this->checkPageUnavailableHandler()) {
1592  $this->pageUnavailableAndExit($message);
1593  } else {
1594  $rootline = '(' . $this->sys_page->error_getRootLine . ')';
1595  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1596  throw new ServiceUnavailableException($message . '<br /><br />' . $rootline, 1301648167);
1597  }
1598  }
1599  $this->fePreview = 1;
1600  }
1601  // Checking for include section regarding the hidden/starttime/endtime/fe_user (that is access control of a whole subbranch!)
1602  if ($this->checkRootlineForIncludeSection()) {
1603  if (empty($this->rootLine)) {
1604  $message = 'The requested page was not accessible!';
1605  if ($this->checkPageUnavailableHandler()) {
1606  $this->pageUnavailableAndExit($message);
1607  } else {
1608  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1609  throw new ServiceUnavailableException($message, 1301648234);
1610  }
1611  } else {
1612  $el = reset($this->rootLine);
1613  $this->id = $el['uid'];
1614  $this->page = $this->sys_page->getPage($this->id);
1615  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1616  }
1617  }
1618  }
1619 
1635  public function getPageShortcut($SC, $mode, $thisUid, $itera = 20, $pageLog = [], $disableGroupCheck = false)
1636  {
1637  $idArray = GeneralUtility::intExplode(',', $SC);
1638  // Find $page record depending on shortcut mode:
1639  switch ($mode) {
1641 
1643  $pageArray = $this->sys_page->getMenu($idArray[0] ? $idArray[0] : $thisUid, '*', 'sorting', 'AND pages.doktype<199 AND pages.doktype!=' . PageRepository::DOKTYPE_BE_USER_SECTION);
1644  $pO = 0;
1645  if ($mode == PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE && !empty($pageArray)) {
1646  $randval = (int)rand(0, count($pageArray) - 1);
1647  $pO = $randval;
1648  }
1649  $c = 0;
1650  $page = [];
1651  foreach ($pageArray as $pV) {
1652  if ($c == $pO) {
1653  $page = $pV;
1654  break;
1655  }
1656  $c++;
1657  }
1658  if (empty($page)) {
1659  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a subpage. ' . 'However, this page has no accessible subpages.';
1660  throw new PageNotFoundException($message, 1301648328);
1661  }
1662  break;
1664  $parent = $this->sys_page->getPage($idArray[0] ? $idArray[0] : $thisUid, $disableGroupCheck);
1665  $page = $this->sys_page->getPage($parent['pid'], $disableGroupCheck);
1666  if (empty($page)) {
1667  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to its parent page. ' . 'However, the parent page is not accessible.';
1668  throw new PageNotFoundException($message, 1301648358);
1669  }
1670  break;
1671  default:
1672  $page = $this->sys_page->getPage($idArray[0], $disableGroupCheck);
1673  if (empty($page)) {
1674  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a page, which is not accessible (ID ' . $idArray[0] . ').';
1675  throw new PageNotFoundException($message, 1301648404);
1676  }
1677  }
1678  // Check if short cut page was a shortcut itself, if so look up recursively:
1679  if ($page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1680  if (!in_array($page['uid'], $pageLog) && $itera > 0) {
1681  $pageLog[] = $page['uid'];
1682  $page = $this->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $page['uid'], $itera - 1, $pageLog, $disableGroupCheck);
1683  } else {
1684  $pageLog[] = $page['uid'];
1685  $message = 'Page shortcuts were looping in uids ' . implode(',', $pageLog) . '...!';
1686  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1687  throw new \RuntimeException($message, 1294587212);
1688  }
1689  }
1690  // Return resulting page:
1691  return $page;
1692  }
1693 
1701  {
1702  $c = count($this->rootLine);
1703  $removeTheRestFlag = 0;
1704  for ($a = 0; $a < $c; $a++) {
1705  if (!$this->checkPagerecordForIncludeSection($this->rootLine[$a])) {
1706  // Add to page access failure history:
1707  $this->pageAccessFailureHistory['sub_section'][] = $this->rootLine[$a];
1708  $removeTheRestFlag = 1;
1709  }
1710 
1711  if ($this->rootLine[$a]['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION) {
1712  // If there is a backend user logged in, check if he has read access to the page:
1713  if ($this->beUserLogin) {
1714  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1715  ->getQueryBuilderForTable('pages');
1716 
1717  $queryBuilder
1718  ->getRestrictions()
1719  ->removeAll();
1720 
1721  $row = $queryBuilder
1722  ->select('uid')
1723  ->from('pages')
1724  ->where(
1725  $queryBuilder->expr()->eq(
1726  'uid',
1727  $queryBuilder->createNamedParameter($this->id, \PDO::PARAM_INT)
1728  ),
1729  $this->getBackendUser()->getPagePermsClause(1)
1730  )
1731  ->execute()
1732  ->fetch();
1733 
1734  // versionOL()?
1735  if (!$row) {
1736  // If there was no page selected, the user apparently did not have read access to the current PAGE (not position in rootline) and we set the remove-flag...
1737  $removeTheRestFlag = 1;
1738  }
1739  } else {
1740  // Dont go here, if there is no backend user logged in.
1741  $removeTheRestFlag = 1;
1742  }
1743  }
1744  if ($removeTheRestFlag) {
1745  // Page is 'not found' in case a subsection was found and not accessible, code 2
1746  $this->pageNotFound = 2;
1747  unset($this->rootLine[$a]);
1748  }
1749  }
1750  return $removeTheRestFlag;
1751  }
1752 
1763  public function checkEnableFields($row, $bypassGroupCheck = false)
1764  {
1765  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'])) {
1766  $_params = ['pObj' => $this, 'row' => &$row, 'bypassGroupCheck' => &$bypassGroupCheck];
1767  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'] as $_funcRef) {
1768  // Call hooks: If one returns FALSE, method execution is aborted with result "This record is not available"
1769  $return = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1770  if ($return === false) {
1771  return false;
1772  }
1773  }
1774  }
1775  if ((!$row['hidden'] || $this->showHiddenPage) && $row['starttime'] <= $GLOBALS['SIM_ACCESS_TIME'] && ($row['endtime'] == 0 || $row['endtime'] > $GLOBALS['SIM_ACCESS_TIME']) && ($bypassGroupCheck || $this->checkPageGroupAccess($row))) {
1776  return true;
1777  }
1778  return false;
1779  }
1780 
1789  public function checkPageGroupAccess($row, $groupList = null)
1790  {
1791  if (is_null($groupList)) {
1792  $groupList = $this->gr_list;
1793  }
1794  if (!is_array($groupList)) {
1795  $groupList = explode(',', $groupList);
1796  }
1797  $pageGroupList = explode(',', $row['fe_group'] ?: 0);
1798  return count(array_intersect($groupList, $pageGroupList)) > 0;
1799  }
1800 
1809  public function checkPagerecordForIncludeSection($row)
1810  {
1811  return !$row['extendToSubpages'] || $this->checkEnableFields($row) ? 1 : 0;
1812  }
1813 
1820  {
1821  // Initialize:
1822  $c = count($this->rootLine);
1823  $loginAllowed = true;
1824  // Traverse root line from root and outwards:
1825  for ($a = 0; $a < $c; $a++) {
1826  // If a value is set for login state:
1827  if ($this->rootLine[$a]['fe_login_mode'] > 0) {
1828  // Determine state from value:
1829  if ((int)$this->rootLine[$a]['fe_login_mode'] === 1) {
1830  $loginAllowed = false;
1831  $this->loginAllowedInBranch_mode = 'all';
1832  } elseif ((int)$this->rootLine[$a]['fe_login_mode'] === 3) {
1833  $loginAllowed = false;
1834  $this->loginAllowedInBranch_mode = 'groups';
1835  } else {
1836  $loginAllowed = true;
1837  }
1838  }
1839  }
1840  return $loginAllowed;
1841  }
1842 
1849  {
1850  $output = [];
1851  $combinedRecords = array_merge(is_array($this->pageAccessFailureHistory['direct_access']) ? $this->pageAccessFailureHistory['direct_access'] : [['fe_group' => 0]], is_array($this->pageAccessFailureHistory['sub_section']) ? $this->pageAccessFailureHistory['sub_section'] : []);
1852  if (!empty($combinedRecords)) {
1853  foreach ($combinedRecords as $k => $pagerec) {
1854  // If $k=0 then it is the very first page the original ID was pointing at and that will get a full check of course
1855  // If $k>0 it is parent pages being tested. They are only significant for the access to the first page IF they had the extendToSubpages flag set, hence checked only then!
1856  if (!$k || $pagerec['extendToSubpages']) {
1857  if ($pagerec['hidden']) {
1858  $output['hidden'][$pagerec['uid']] = true;
1859  }
1860  if ($pagerec['starttime'] > $GLOBALS['SIM_ACCESS_TIME']) {
1861  $output['starttime'][$pagerec['uid']] = $pagerec['starttime'];
1862  }
1863  if ($pagerec['endtime'] != 0 && $pagerec['endtime'] <= $GLOBALS['SIM_ACCESS_TIME']) {
1864  $output['endtime'][$pagerec['uid']] = $pagerec['endtime'];
1865  }
1866  if (!$this->checkPageGroupAccess($pagerec)) {
1867  $output['fe_group'][$pagerec['uid']] = $pagerec['fe_group'];
1868  }
1869  }
1870  }
1871  }
1872  return $output;
1873  }
1874 
1884  {
1885  $this->getPageAndRootline();
1886  // Checks if the $domain-startpage is in the rootLine. This is necessary so that references to page-id's from other domains are not possible.
1887  if ($domainStartPage && is_array($this->rootLine)) {
1888  $idFound = 0;
1889  foreach ($this->rootLine as $key => $val) {
1890  if ($val['uid'] == $domainStartPage) {
1891  $idFound = 1;
1892  break;
1893  }
1894  }
1895  if (!$idFound) {
1896  // Page is 'not found' in case the id was outside the domain, code 3
1897  $this->pageNotFound = 3;
1898  $this->id = $domainStartPage;
1899  // re-get the page and rootline if the id was not found.
1900  $this->getPageAndRootline();
1901  }
1902  }
1903  }
1904 
1911  public function setSysPageWhereClause()
1912  {
1913  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
1914  ->getConnectionForTable('pages')
1915  ->getExpressionBuilder();
1916  $this->sys_page->where_hid_del = ' AND ' . (string)$expressionBuilder->andX(
1917  QueryHelper::stripLogicalOperatorPrefix($this->sys_page->where_hid_del),
1918  $expressionBuilder->lt('pages.doktype', 200)
1919  );
1920  $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages');
1921  }
1922 
1930  public function findDomainRecord($recursive = false)
1931  {
1932  if ($recursive) {
1933  $pageUid = 0;
1934  $host = explode('.', GeneralUtility::getIndpEnv('HTTP_HOST'));
1935  while (count($host)) {
1936  $pageUid = $this->sys_page->getDomainStartPage(implode('.', $host), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1937  if ($pageUid) {
1938  return $pageUid;
1939  } else {
1940  array_shift($host);
1941  }
1942  }
1943  return $pageUid;
1944  } else {
1945  return $this->sys_page->getDomainStartPage(GeneralUtility::getIndpEnv('HTTP_HOST'), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1946  }
1947  }
1948 
1956  public function pageUnavailableAndExit($reason = '', $header = '')
1957  {
1958  $header = $header ?: $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling_statheader'];
1959  $this->pageUnavailableHandler($GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling'], $header, $reason);
1960  die;
1961  }
1962 
1970  public function pageNotFoundAndExit($reason = '', $header = '')
1971  {
1972  $header = $header ?: $GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling_statheader'];
1973  $this->pageNotFoundHandler($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFound_handling'], $header, $reason);
1974  die;
1975  }
1976 
1984  {
1985  if (
1986  $GLOBALS['TYPO3_CONF_VARS']['FE']['pageUnavailable_handling']
1988  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
1989  $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
1990  )
1991  ) {
1992  $checkPageUnavailableHandler = true;
1993  } else {
1994  $checkPageUnavailableHandler = false;
1995  }
1996  return $checkPageUnavailableHandler;
1997  }
1998 
2007  public function pageUnavailableHandler($code, $header, $reason)
2008  {
2009  $this->pageErrorHandler($code, $header, $reason);
2010  }
2011 
2020  public function pageNotFoundHandler($code, $header = '', $reason = '')
2021  {
2022  $this->pageErrorHandler($code, $header, $reason);
2023  }
2024 
2035  public function pageErrorHandler($code, $header = '', $reason = '')
2036  {
2037  // Issue header in any case:
2038  if ($header) {
2039  $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2040  foreach ($headerArr as $header) {
2041  header($header);
2042  }
2043  }
2044  // Create response:
2045  // Simply boolean; Just shows TYPO3 error page with reason:
2046  if (strtolower($code) === 'true' || (string)$code === '1' || gettype($code) === 'boolean') {
2047  echo GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
2048  'Page Not Found',
2049  'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . $reason : '')
2050  );
2051  } elseif (GeneralUtility::isFirstPartOfStr($code, 'USER_FUNCTION:')) {
2052  $funcRef = trim(substr($code, 14));
2053  $params = [
2054  'currentUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
2055  'reasonText' => $reason,
2056  'pageAccessFailureReasons' => $this->getPageAccessFailureReasons()
2057  ];
2058  echo GeneralUtility::callUserFunction($funcRef, $params, $this);
2059  } elseif (GeneralUtility::isFirstPartOfStr($code, 'READFILE:')) {
2060  $readFile = GeneralUtility::getFileAbsFileName(trim(substr($code, 9)));
2061  if (@is_file($readFile)) {
2062  echo str_replace(
2063  [
2064  '###CURRENT_URL###',
2065  '###REASON###'
2066  ],
2067  [
2068  GeneralUtility::getIndpEnv('REQUEST_URI'),
2069  htmlspecialchars($reason)
2070  ],
2071  file_get_contents($readFile)
2072  );
2073  } else {
2074  throw new \RuntimeException('Configuration Error: 404 page "' . $readFile . '" could not be found.', 1294587214);
2075  }
2076  } elseif (GeneralUtility::isFirstPartOfStr($code, 'REDIRECT:')) {
2077  HttpUtility::redirect(substr($code, 9));
2078  } elseif ($code !== '') {
2079  // Check if URL is relative
2080  $url_parts = parse_url($code);
2081  if ($url_parts['host'] == '') {
2082  $url_parts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
2083  if ($code[0] === '/') {
2084  $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . $code;
2085  } else {
2086  $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR') . $code;
2087  }
2088  $checkBaseTag = false;
2089  } else {
2090  $checkBaseTag = true;
2091  }
2092  // Check recursion
2093  if ($code == GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
2094  if ($reason == '') {
2095  $reason = 'Page cannot be found.';
2096  }
2097  $reason .= LF . LF . 'Additionally, ' . $code . ' was not found while trying to retrieve the error document.';
2098  throw new \RuntimeException(nl2br(htmlspecialchars($reason)), 1294587215);
2099  }
2100  // Prepare headers
2101  $headerArr = [
2102  'User-agent: ' . GeneralUtility::getIndpEnv('HTTP_USER_AGENT'),
2103  'Referer: ' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')
2104  ];
2105  $res = GeneralUtility::getUrl($code, 1, $headerArr);
2106  // Header and content are separated by an empty line
2107  list($header, $content) = explode(CRLF . CRLF, $res, 2);
2108  $content .= CRLF;
2109  if (false === $res) {
2110  // Last chance -- redirect
2111  HttpUtility::redirect($code);
2112  } else {
2113  // Forward these response headers to the client
2114  $forwardHeaders = [
2115  'Content-Type:'
2116  ];
2117  $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2118  foreach ($headerArr as $header) {
2119  foreach ($forwardHeaders as $h) {
2120  if (preg_match('/^' . $h . '/', $header)) {
2121  header($header);
2122  }
2123  }
2124  }
2125  // Put <base> if necesary
2126  if ($checkBaseTag) {
2127  // If content already has <base> tag, we do not need to do anything
2128  if (false === stristr($content, '<base ')) {
2129  // Generate href for base tag
2130  $base = $url_parts['scheme'] . '://';
2131  if ($url_parts['user'] != '') {
2132  $base .= $url_parts['user'];
2133  if ($url_parts['pass'] != '') {
2134  $base .= ':' . $url_parts['pass'];
2135  }
2136  $base .= '@';
2137  }
2138  $base .= $url_parts['host'];
2139  // Add path portion skipping possible file name
2140  $base .= preg_replace('/(.*\\/)[^\\/]*/', '${1}', $url_parts['path']);
2141  // Put it into content (generate also <head> if necessary)
2142  $replacement = LF . '<base href="' . htmlentities($base) . '" />' . LF;
2143  if (stristr($content, '<head>')) {
2144  $content = preg_replace('/(<head>)/i', '\\1' . $replacement, $content);
2145  } else {
2146  $content = preg_replace('/(<html[^>]*>)/i', '\\1<head>' . $replacement . '</head>', $content);
2147  }
2148  }
2149  }
2150  // Output the content
2151  echo $content;
2152  }
2153  } else {
2154  echo GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
2155  'Page Not Found',
2156  $reason ? 'Reason: ' . $reason : 'Page cannot be found.'
2157  );
2158  }
2159  die;
2160  }
2161 
2169  public function checkAndSetAlias()
2170  {
2171  if ($this->id && !MathUtility::canBeInterpretedAsInteger($this->id)) {
2172  $aid = $this->sys_page->getPageIdFromAlias($this->id);
2173  if ($aid) {
2174  $this->id = $aid;
2175  } else {
2176  $this->pageNotFound = 4;
2177  }
2178  }
2179  }
2180 
2187  public function mergingWithGetVars($GET_VARS)
2188  {
2189  if (is_array($GET_VARS)) {
2190  // Getting $_GET var, unescaped.
2191  $realGet = GeneralUtility::_GET();
2192  if (!is_array($realGet)) {
2193  $realGet = [];
2194  }
2195  // Merge new values on top:
2196  ArrayUtility::mergeRecursiveWithOverrule($realGet, $GET_VARS);
2197  // Write values back to $_GET:
2198  GeneralUtility::_GETset($realGet);
2199  // Setting these specifically (like in the init-function):
2200  if (isset($GET_VARS['type'])) {
2201  $this->type = (int)$GET_VARS['type'];
2202  }
2203  if (isset($GET_VARS['cHash'])) {
2204  $this->cHash = $GET_VARS['cHash'];
2205  }
2206  if (isset($GET_VARS['MP'])) {
2207  $this->MP = $GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids'] ? $GET_VARS['MP'] : '';
2208  }
2209  if (isset($GET_VARS['no_cache']) && $GET_VARS['no_cache']) {
2210  $this->set_no_cache('no_cache is requested via GET parameter');
2211  }
2212  }
2213  }
2214 
2215  /********************************************
2216  *
2217  * Template and caching related functions.
2218  *
2219  *******************************************/
2229  public function makeCacheHash()
2230  {
2231  // No need to test anything if caching was already disabled.
2232  if ($this->no_cache && !$GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError']) {
2233  return;
2234  }
2235  $GET = GeneralUtility::_GET();
2236  if ($this->cHash && is_array($GET)) {
2237  // Make sure we use the page uid and not the page alias
2238  $GET['id'] = $this->id;
2239  $this->cHash_array = $this->cacheHash->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $GET));
2240  $cHash_calc = $this->cacheHash->calculateCacheHash($this->cHash_array);
2241  if ($cHash_calc != $this->cHash) {
2242  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError']) {
2243  $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash comparison failed)');
2244  } else {
2245  $this->disableCache();
2246  $this->getTimeTracker()->setTSlogMessage('The incoming cHash "' . $this->cHash . '" and calculated cHash "' . $cHash_calc . '" did not match, so caching was disabled. The fieldlist used was "' . implode(',', array_keys($this->cHash_array)) . '"', 2);
2247  }
2248  }
2249  } elseif (is_array($GET)) {
2250  // No cHash is set, check if that is correct
2251  if ($this->cacheHash->doParametersRequireCacheHash(GeneralUtility::implodeArrayForUrl('', $GET))) {
2252  $this->reqCHash();
2253  }
2254  }
2255  }
2256 
2264  public function reqCHash()
2265  {
2266  if (!$this->cHash) {
2267  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageNotFoundOnCHashError']) {
2268  if ($this->tempContent) {
2269  $this->clearPageCacheContent();
2270  }
2271  $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash empty)');
2272  } else {
2273  $this->disableCache();
2274  $this->getTimeTracker()->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled', 2);
2275  }
2276  }
2277  }
2278 
2284  public function initTemplate()
2285  {
2286  $this->tmpl = GeneralUtility::makeInstance(TemplateService::class);
2287  $this->tmpl->setVerbose((bool)$this->beUserLogin);
2288  $this->tmpl->init();
2289  $this->tmpl->tt_track = (bool)$this->beUserLogin;
2290  }
2291 
2299  public function getFromCache()
2300  {
2301  // clearing the content-variable, which will hold the pagecontent
2302  $this->content = '';
2303  // Unsetting the lowlevel config
2304  unset($this->config);
2305  $this->cacheContentFlag = false;
2306 
2307  if ($this->no_cache) {
2308  return;
2309  }
2310 
2311  $pageSectionCacheContent = $this->tmpl->getCurrentPageData();
2312  if (!is_array($pageSectionCacheContent)) {
2313  // Nothing in the cache, we acquire an "exclusive lock" for the key now.
2314  // We use the Registry to store this lock centrally,
2315  // but we protect the access again with a global exclusive lock to avoid race conditions
2316 
2317  $this->acquireLock('pagesection', $this->id . '::' . $this->MP);
2318  //
2319  // from this point on we're the only one working on that page ($key)
2320  //
2321 
2322  // query the cache again to see if the page data are there meanwhile
2323  $pageSectionCacheContent = $this->tmpl->getCurrentPageData();
2324  if (is_array($pageSectionCacheContent)) {
2325  // we have the content, nice that some other process did the work for us already
2326  $this->releaseLock('pagesection');
2327  } else {
2328  // We keep the lock set, because we are the ones generating the page now
2329  // and filling the cache.
2330  // This indicates that we have to release the lock in the Registry later in releaseLocks()
2331  }
2332  }
2333 
2334  if (is_array($pageSectionCacheContent)) {
2335  // BE CAREFUL to change the content of the cc-array. This array is serialized and an md5-hash based on this is used for caching the page.
2336  // If this hash is not the same in here in this section and after page-generation, then the page will not be properly cached!
2337  // This array is an identification of the template. If $this->all is empty it's because the template-data is not cached, which it must be.
2338  $pageSectionCacheContent = $this->tmpl->matching($pageSectionCacheContent);
2339  ksort($pageSectionCacheContent);
2340  $this->all = $pageSectionCacheContent;
2341  }
2342  unset($pageSectionCacheContent);
2343 
2344  // Look for page in cache only if a shift-reload is not sent to the server.
2345  $lockHash = $this->getLockHash();
2346  if (!$this->headerNoCache()) {
2347  if ($this->all) {
2348  // we got page section information
2349  $this->newHash = $this->getHash();
2350  $this->getTimeTracker()->push('Cache Row', '');
2351  $row = $this->getFromCache_queryRow();
2352  if (!is_array($row)) {
2353  // nothing in the cache, we acquire an exclusive lock now
2354 
2355  $this->acquireLock('pages', $lockHash);
2356  //
2357  // from this point on we're the only one working on that page ($lockHash)
2358  //
2359 
2360  // query the cache again to see if the data are there meanwhile
2361  $row = $this->getFromCache_queryRow();
2362  if (is_array($row)) {
2363  // we have the content, nice that some other process did the work for us
2364  $this->releaseLock('pages');
2365  } else {
2366  // We keep the lock set, because we are the ones generating the page now
2367  // and filling the cache.
2368  // This indicates that we have to release the lock in the Registry later in releaseLocks()
2369  }
2370  }
2371  if (is_array($row)) {
2372  // we have data from cache
2373 
2374  // Call hook when a page is retrieved from cache:
2375  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'])) {
2376  $_params = ['pObj' => &$this, 'cache_pages_row' => &$row];
2377  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'] as $_funcRef) {
2378  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2379  }
2380  }
2381  // Fetches the lowlevel config stored with the cached data
2382  $this->config = $row['cache_data'];
2383  // Getting the content
2384  $this->content = $row['content'];
2385  // Flag for temp content
2386  $this->tempContent = $row['temp_content'];
2387  // Setting flag, so we know, that some cached content has been loaded
2388  $this->cacheContentFlag = true;
2389  $this->cacheExpires = $row['expires'];
2390 
2391  // Restore page title information, this is needed to generate the page title for
2392  // partially cached pages.
2393  $this->page['title'] = $row['pageTitleInfo']['title'];
2394  $this->altPageTitle = $row['pageTitleInfo']['altPageTitle'];
2395  $this->indexedDocTitle = $row['pageTitleInfo']['indexedDocTitle'];
2396 
2397  if (isset($this->config['config']['debug'])) {
2398  $debugCacheTime = (bool)$this->config['config']['debug'];
2399  } else {
2400  $debugCacheTime = !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']);
2401  }
2402  if ($debugCacheTime) {
2403  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
2404  $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
2405  $this->content .= LF . '<!-- Cached page generated ' . date(($dateFormat . ' ' . $timeFormat), $row['tstamp']) . '. Expires ' . date(($dateFormat . ' ' . $timeFormat), $row['expires']) . ' -->';
2406  }
2407  }
2408  $this->getTimeTracker()->pull();
2409 
2410  return;
2411  }
2412  }
2413  // the user forced rebuilding the page cache or there was no pagesection information
2414  // get a lock for the page content so other processes will not interrupt the regeneration
2415  $this->acquireLock('pages', $lockHash);
2416  }
2417 
2423  public function getFromCache_queryRow()
2424  {
2425  $this->getTimeTracker()->push('Cache Query', '');
2426  $row = $this->pageCache->get($this->newHash);
2427  $this->getTimeTracker()->pull();
2428  return $row;
2429  }
2430 
2438  public function headerNoCache()
2439  {
2440  $disableAcquireCacheData = false;
2441  if ($this->beUserLogin) {
2442  if (strtolower($_SERVER['HTTP_CACHE_CONTROL']) === 'no-cache' || strtolower($_SERVER['HTTP_PRAGMA']) === 'no-cache') {
2443  $disableAcquireCacheData = true;
2444  }
2445  }
2446  // Call hook for possible by-pass of requiring of page cache (for recaching purpose)
2447  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'])) {
2448  $_params = ['pObj' => &$this, 'disableAcquireCacheData' => &$disableAcquireCacheData];
2449  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'] as $_funcRef) {
2450  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2451  }
2452  }
2453  return $disableAcquireCacheData;
2454  }
2455 
2465  public function getHash()
2466  {
2467  return md5($this->createHashBase(false));
2468  }
2469 
2478  public function getLockHash()
2479  {
2480  $lockHash = $this->createHashBase(true);
2481  return md5($lockHash);
2482  }
2483 
2494  protected function createHashBase($createLockHashBase = false)
2495  {
2496  $hashParameters = [
2497  'id' => (int)$this->id,
2498  'type' => (int)$this->type,
2499  'gr_list' => (string)$this->gr_list,
2500  'MP' => (string)$this->MP,
2501  'cHash' => $this->cHash_array,
2502  'domainStartPage' => $this->domainStartPage
2503  ];
2504  // Include the template information if we shouldn't create a lock hash
2505  if (!$createLockHashBase) {
2506  $hashParameters['all'] = $this->all;
2507  }
2508  // Call hook to influence the hash calculation
2509  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'])) {
2510  $_params = [
2511  'hashParameters' => &$hashParameters,
2512  'createLockHashBase' => $createLockHashBase
2513  ];
2514  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'] as $_funcRef) {
2515  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2516  }
2517  }
2518  return serialize($hashParameters);
2519  }
2520 
2527  public function getConfigArray()
2528  {
2529  // If config is not set by the cache (which would be a major mistake somewhere) OR if INTincScripts-include-scripts have been registered, then we must parse the template in order to get it
2530  if (!is_array($this->config) || is_array($this->config['INTincScript']) || $this->forceTemplateParsing) {
2531  $timeTracker = $this->getTimeTracker();
2532  $timeTracker->push('Parse template', '');
2533  // Force parsing, if set?:
2534  $this->tmpl->forceTemplateParsing = $this->forceTemplateParsing;
2535  // Start parsing the TS template. Might return cached version.
2536  $this->tmpl->start($this->rootLine);
2537  $timeTracker->pull();
2538  if ($this->tmpl->loaded) {
2539  $timeTracker->push('Setting the config-array', '');
2540  // toplevel - objArrayName
2541  $this->sPre = $this->tmpl->setup['types.'][$this->type];
2542  $this->pSetup = $this->tmpl->setup[$this->sPre . '.'];
2543  if (!is_array($this->pSetup)) {
2544  $message = 'The page is not configured! [type=' . $this->type . '][' . $this->sPre . '].';
2545  if ($this->checkPageUnavailableHandler()) {
2546  $this->pageUnavailableAndExit($message);
2547  } else {
2548  $explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
2549  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2550  throw new ServiceUnavailableException($message . ' ' . $explanation, 1294587217);
2551  }
2552  } else {
2553  if (!isset($this->config['config'])) {
2554  $this->config['config'] = [];
2555  }
2556  // Filling the config-array, first with the main "config." part
2557  if (is_array($this->tmpl->setup['config.'])) {
2558  ArrayUtility::mergeRecursiveWithOverrule($this->tmpl->setup['config.'], $this->config['config']);
2559  $this->config['config'] = $this->tmpl->setup['config.'];
2560  }
2561  // override it with the page/type-specific "config."
2562  if (is_array($this->pSetup['config.'])) {
2563  ArrayUtility::mergeRecursiveWithOverrule($this->config['config'], $this->pSetup['config.']);
2564  }
2565  if ($this->config['config']['typolinkEnableLinksAcrossDomains']) {
2566  $this->config['config']['typolinkCheckRootline'] = true;
2567  }
2568  // Set default values for removeDefaultJS and inlineStyle2TempFile so CSS and JS are externalized if compatversion is higher than 4.0
2569  if (!isset($this->config['config']['removeDefaultJS'])) {
2570  $this->config['config']['removeDefaultJS'] = 'external';
2571  }
2572  if (!isset($this->config['config']['inlineStyle2TempFile'])) {
2573  $this->config['config']['inlineStyle2TempFile'] = 1;
2574  }
2575 
2576  if (!isset($this->config['config']['compressJs'])) {
2577  $this->config['config']['compressJs'] = 0;
2578  }
2579  // Processing for the config_array:
2580  $this->config['rootLine'] = $this->tmpl->rootLine;
2581  $this->config['mainScript'] = trim($this->config['config']['mainScript']) ?: 'index.php';
2582  if (isset($this->config['config']['mainScript']) || $this->config['mainScript'] !== 'index.php') {
2583  $this->logDeprecatedTyposcript('config.mainScript', 'Setting the frontend script to something else than index.php is deprecated as of TYPO3 v8, and will not be possible in TYPO3 v9 without a custom extension');
2584  }
2585  // Class for render Header and Footer parts
2586  if ($this->pSetup['pageHeaderFooterTemplateFile']) {
2587  $file = $this->tmpl->getFileName($this->pSetup['pageHeaderFooterTemplateFile']);
2588  if ($file) {
2589  $this->pageRenderer->setTemplateFile($file);
2590  }
2591  }
2592  }
2593  $timeTracker->pull();
2594  } else {
2595  if ($this->checkPageUnavailableHandler()) {
2596  $this->pageUnavailableAndExit('No TypoScript template found!');
2597  } else {
2598  $message = 'No TypoScript template found!';
2599  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2600  throw new ServiceUnavailableException($message, 1294587218);
2601  }
2602  }
2603  }
2604 
2605  // No cache
2606  // Set $this->no_cache TRUE if the config.no_cache value is set!
2607  if ($this->config['config']['no_cache']) {
2608  $this->set_no_cache('config.no_cache is set');
2609  }
2610  // Merge GET with defaultGetVars
2611  if (!empty($this->config['config']['defaultGetVars.'])) {
2612  $modifiedGetVars = GeneralUtility::removeDotsFromTS($this->config['config']['defaultGetVars.']);
2614  GeneralUtility::_GETset($modifiedGetVars);
2615  }
2616  // Hook for postProcessing the configuration array
2617  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'])) {
2618  $params = ['config' => &$this->config['config']];
2619  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'] as $funcRef) {
2620  GeneralUtility::callUserFunction($funcRef, $params, $this);
2621  }
2622  }
2623  }
2624 
2625  /********************************************
2626  *
2627  * Further initialization and data processing
2628  *
2629  *******************************************/
2630 
2638  public function settingLanguage()
2639  {
2640  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'])) {
2641  $_params = [];
2642  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'] as $_funcRef) {
2643  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2644  }
2645  }
2646 
2647  // Initialize charset settings etc.
2648  $this->initLLvars();
2649 
2650  // Get values from TypoScript:
2651  $this->sys_language_uid = ($this->sys_language_content = (int)$this->config['config']['sys_language_uid']);
2652  list($this->sys_language_mode, $sys_language_content) = GeneralUtility::trimExplode(';', $this->config['config']['sys_language_mode']);
2653  $this->sys_language_contentOL = $this->config['config']['sys_language_overlay'];
2654  // If sys_language_uid is set to another language than default:
2655  if ($this->sys_language_uid > 0) {
2656  // check whether a shortcut is overwritten by a translated page
2657  // we can only do this now, as this is the place where we get
2658  // to know about translations
2659  $this->checkTranslatedShortcut();
2660  // Request the overlay record for the sys_language_uid:
2661  $olRec = $this->sys_page->getPageOverlay($this->id, $this->sys_language_uid);
2662  if (empty($olRec)) {
2663  // If no OL record exists and a foreign language is asked for...
2664  if ($this->sys_language_uid) {
2665  // If requested translation is not available:
2666  if (GeneralUtility::hideIfNotTranslated($this->page['l18n_cfg'])) {
2667  $this->pageNotFoundAndExit('Page is not available in the requested language.');
2668  } else {
2669  switch ((string)$this->sys_language_mode) {
2670  case 'strict':
2671  $this->pageNotFoundAndExit('Page is not available in the requested language (strict).');
2672  break;
2673  case 'content_fallback':
2674  $fallBackOrder = GeneralUtility::intExplode(',', $sys_language_content);
2675  foreach ($fallBackOrder as $orderValue) {
2676  if ((string)$orderValue === '0' || !empty($this->sys_page->getPageOverlay($this->id, $orderValue))) {
2677  $this->sys_language_content = $orderValue;
2678  // Setting content uid (but leaving the sys_language_uid)
2679  break;
2680  }
2681  }
2682  break;
2683  case 'ignore':
2684  $this->sys_language_content = $this->sys_language_uid;
2685  break;
2686  default:
2687  // Default is that everything defaults to the default language...
2688  $this->sys_language_uid = ($this->sys_language_content = 0);
2689  }
2690  }
2691  }
2692  } else {
2693  // Setting sys_language if an overlay record was found (which it is only if a language is used)
2694  $this->page = $this->sys_page->getPageOverlay($this->page, $this->sys_language_uid);
2695  }
2696  }
2697  // Setting sys_language_uid inside sys-page:
2698  $this->sys_page->sys_language_uid = $this->sys_language_uid;
2699  // If default translation is not available:
2700  if ((!$this->sys_language_uid || !$this->sys_language_content) && GeneralUtility::hideIfDefaultLanguage($this->page['l18n_cfg'])) {
2701  $message = 'Page is not available in default language.';
2702  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2703  $this->pageNotFoundAndExit($message);
2704  }
2706 
2707  // Finding the ISO code for the currently selected language
2708  // fetched by the sys_language record when not fetching content from the default language
2709  if ($this->sys_language_content > 0) {
2710  // using sys_language_content because the ISO code only (currently) affect content selection from FlexForms - which should follow "sys_language_content"
2711  // Set the fourth parameter to TRUE in the next two getRawRecord() calls to
2712  // avoid versioning overlay to be applied as it generates an SQL error
2713  $sys_language_row = $this->sys_page->getRawRecord('sys_language', $this->sys_language_content, 'language_isocode,static_lang_isocode', true);
2714  if (is_array($sys_language_row) && !empty($sys_language_row['language_isocode'])) {
2715  $this->sys_language_isocode = $sys_language_row['language_isocode'];
2716  }
2717  // the DB value is overriden by TypoScript
2718  if (!empty($this->config['config']['sys_language_isocode'])) {
2719  $this->sys_language_isocode = $this->config['config']['sys_language_isocode'];
2720  }
2721  } else {
2722  // fallback to the TypoScript option when rendering with sys_language_uid=0
2723  // also: use "en" by default
2724  if (!empty($this->config['config']['sys_language_isocode_default'])) {
2725  $this->sys_language_isocode = $this->config['config']['sys_language_isocode_default'];
2726  } else {
2727  $this->sys_language_isocode = $this->lang != 'default' ? $this->lang : 'en';
2728  }
2729  }
2730 
2731  // Setting softMergeIfNotBlank:
2732  $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softMergeIfNotBlank'], true);
2733  foreach ($table_fields as $TF) {
2734  list($tN, $fN) = explode(':', $TF);
2735  $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'mergeIfNotBlank';
2736  }
2737  // Setting softExclude:
2738  $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softExclude'], true);
2739  foreach ($table_fields as $TF) {
2740  list($tN, $fN) = explode(':', $TF);
2741  $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'exclude';
2742  }
2743  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'])) {
2744  $_params = [];
2745  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'] as $_funcRef) {
2746  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2747  }
2748  }
2749  }
2750 
2754  protected function updateRootLinesWithTranslations()
2755  {
2756  if ($this->sys_language_uid) {
2757  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
2758  $this->tmpl->updateRootlineData($this->rootLine);
2759  }
2760  }
2761 
2767  public function settingLocale()
2768  {
2769  // Setting locale
2770  if ($this->config['config']['locale_all']) {
2771  $availableLocales = GeneralUtility::trimExplode(',', $this->config['config']['locale_all'], true);
2772  // If LC_NUMERIC is set e.g. to 'de_DE' PHP parses float values locale-aware resulting in strings with comma
2773  // as decimal point which causes problems with value conversions - so we set all locale types except LC_NUMERIC
2774  // @see https://bugs.php.net/bug.php?id=53711
2775  $locale = setlocale(LC_COLLATE, ...$availableLocales);
2776  if ($locale) {
2777  // As str_* methods are locale aware and turkish has no upper case I
2778  // Class autoloading and other checks depending on case changing break with turkish locale LC_CTYPE
2779  // @see http://bugs.php.net/bug.php?id=35050
2780  if (substr($this->config['config']['locale_all'], 0, 2) != 'tr') {
2781  setlocale(LC_CTYPE, ...$availableLocales);
2782  }
2783  setlocale(LC_MONETARY, ...$availableLocales);
2784  setlocale(LC_TIME, ...$availableLocales);
2785  } else {
2786  $this->getTimeTracker()->setTSlogMessage('Locale "' . htmlspecialchars($this->config['config']['locale_all']) . '" not found.', 3);
2787  }
2788  }
2789  }
2790 
2799  protected function checkTranslatedShortcut()
2800  {
2801  if (!is_null($this->originalShortcutPage)) {
2802  $originalShortcutPageOverlay = $this->sys_page->getPageOverlay($this->originalShortcutPage['uid'], $this->sys_language_uid);
2803  if (!empty($originalShortcutPageOverlay['shortcut']) && $originalShortcutPageOverlay['shortcut'] != $this->id) {
2804  // the translation of the original shortcut page has a different shortcut target!
2805  // set the correct page and id
2806  $shortcut = $this->getPageShortcut($originalShortcutPageOverlay['shortcut'], $originalShortcutPageOverlay['shortcut_mode'], $originalShortcutPageOverlay['uid']);
2807  $this->id = ($this->contentPid = $shortcut['uid']);
2808  $this->page = $this->sys_page->getPage($this->id);
2809  // Fix various effects on things like menus f.e.
2810  $this->fetch_the_id();
2811  $this->tmpl->rootLine = array_reverse($this->rootLine);
2812  }
2813  }
2814  }
2815 
2822  public function handleDataSubmission()
2823  {
2824  // Hook for processing data submission to extensions
2825  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'])) {
2826  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] as $_classRef) {
2827  $_procObj = GeneralUtility::getUserObj($_classRef);
2828  $_procObj->checkDataSubmission($this);
2829  }
2830  }
2831  }
2832 
2838  public function initializeRedirectUrlHandlers()
2839  {
2840  if (
2841  empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'])
2842  || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'])
2843  ) {
2844  return;
2845  }
2846 
2847  $urlHandlers = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'];
2848  foreach ($urlHandlers as $identifier => $configuration) {
2849  if (empty($configuration) || !is_array($configuration)) {
2850  throw new \RuntimeException('Missing configuration for URL handler "' . $identifier . '".', 1442052263);
2851  }
2852  if (!is_string($configuration['handler']) || empty($configuration['handler']) || !class_exists($configuration['handler']) || !is_subclass_of($configuration['handler'], UrlHandlerInterface::class)) {
2853  throw new \RuntimeException('The URL handler "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlHandlerInterface::class . '".', 1442052249);
2854  }
2855  }
2856 
2857  $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlHandlers);
2858 
2859  foreach ($orderedHandlers as $configuration) {
2861  $urlHandler = GeneralUtility::makeInstance($configuration['handler']);
2862  if ($urlHandler->canHandleCurrentUrl()) {
2863  $this->activeUrlHandlers[] = $urlHandler;
2864  }
2865  }
2866  }
2867 
2876  public function redirectToExternalUrl()
2877  {
2878  foreach ($this->activeUrlHandlers as $redirectHandler) {
2879  $redirectHandler->handle();
2880  }
2881 
2882  if (!empty($this->activeUrlHandlers)) {
2883  throw new \RuntimeException('A URL handler is active but did not process the URL.', 1442305505);
2884  }
2885  }
2886 
2894  public function setUrlIdToken()
2895  {
2896  if ($this->config['config']['ftu']) {
2897  $this->getMethodUrlIdToken = $GLOBALS['TYPO3_CONF_VARS']['FE']['get_url_id_token'];
2898  } else {
2899  $this->getMethodUrlIdToken = '';
2900  }
2901  }
2902 
2909  public function calculateLinkVars()
2910  {
2911  $this->linkVars = '';
2912  $linkVars = GeneralUtility::trimExplode(',', (string)$this->config['config']['linkVars']);
2913  if (empty($linkVars)) {
2914  return;
2915  }
2916  $getData = GeneralUtility::_GET();
2917  foreach ($linkVars as $linkVar) {
2918  $test = ($value = '');
2919  if (preg_match('/^(.*)\\((.+)\\)$/', $linkVar, $match)) {
2920  $linkVar = trim($match[1]);
2921  $test = trim($match[2]);
2922  }
2923  if ($linkVar === '' || !isset($getData[$linkVar])) {
2924  continue;
2925  }
2926  if (!is_array($getData[$linkVar])) {
2927  $temp = rawurlencode($getData[$linkVar]);
2928  if ($test !== '' && !PageGenerator::isAllowedLinkVarValue($temp, $test)) {
2929  // Error: This value was not allowed for this key
2930  continue;
2931  }
2932  $value = '&' . $linkVar . '=' . $temp;
2933  } else {
2934  if ($test !== '' && $test !== 'array') {
2935  // Error: This key must not be an array!
2936  continue;
2937  }
2938  $value = GeneralUtility::implodeArrayForUrl($linkVar, $getData[$linkVar]);
2939  }
2940  $this->linkVars .= $value;
2941  }
2942  }
2943 
2953  {
2954  if (!empty($this->originalMountPointPage) && $this->originalMountPointPage['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT) {
2955  $this->redirectToCurrentPage();
2956  }
2957  }
2958 
2968  {
2969  if (!empty($this->originalShortcutPage) && $this->originalShortcutPage['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
2970  $this->redirectToCurrentPage();
2971  }
2972  }
2973 
2980  protected function redirectToCurrentPage()
2981  {
2982  $this->calculateLinkVars();
2983  // Instantiate \TYPO3\CMS\Frontend\ContentObject to generate the correct target URL
2985  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
2986  $parameter = $this->page['uid'];
2987  $type = GeneralUtility::_GET('type');
2989  $parameter .= ',' . $type;
2990  }
2991  $redirectUrl = $cObj->typoLink_URL(['parameter' => $parameter, 'addQueryString' => true,
2992  'addQueryString.' => ['exclude' => 'id']]);
2993 
2994  // Prevent redirection loop
2995  if (!empty($redirectUrl)) {
2996  // redirect and exit
2998  }
2999  }
3000 
3001  /********************************************
3002  *
3003  * Page generation; cache handling
3004  *
3005  *******************************************/
3012  public function isGeneratePage()
3013  {
3014  return !$this->cacheContentFlag && empty($this->activeUrlHandlers);
3015  }
3016 
3023  public function tempPageCacheContent()
3024  {
3025  $this->tempContent = false;
3026  if (!$this->no_cache) {
3027  $seconds = 30;
3028  $title = htmlspecialchars($this->tmpl->printTitle($this->page['title']));
3029  $request_uri = htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI'));
3030  $stdMsg = '
3031  <strong>Page is being generated.</strong><br />
3032  If this message does not disappear within ' . $seconds . ' seconds, please reload.';
3033  $message = $this->config['config']['message_page_is_being_generated'];
3034  if ((string)$message !== '') {
3035  $message = str_replace('###TITLE###', $title, $message);
3036  $message = str_replace('###REQUEST_URI###', $request_uri, $message);
3037  } else {
3038  $message = $stdMsg;
3039  }
3040  $temp_content = '<?xml version="1.0" encoding="UTF-8"?>
3041 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3042  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3043 <html xmlns="http://www.w3.org/1999/xhtml">
3044  <head>
3045  <title>' . $title . '</title>
3046  <meta http-equiv="refresh" content="10" />
3047  </head>
3048  <body style="background-color:white; font-family:Verdana,Arial,Helvetica,sans-serif; color:#cccccc; text-align:center;">' . $message . '
3049  </body>
3050 </html>';
3051  // Fix 'nice errors' feature in modern browsers
3052  $padSuffix = '<!--pad-->';
3053  // prevent any trims
3054  $padSize = 768 - strlen($padSuffix) - strlen($temp_content);
3055  if ($padSize > 0) {
3056  $temp_content = str_pad($temp_content, $padSize, LF) . $padSuffix;
3057  }
3058  if (!$this->headerNoCache() && ($cachedRow = $this->getFromCache_queryRow())) {
3059  // We are here because between checking for cached content earlier and now some other HTTP-process managed to store something in cache AND it was not due to a shift-reload by-pass.
3060  // This is either the "Page is being generated" screen or it can be the final result.
3061  // In any case we should not begin another rendering process also, so we silently disable caching and render the page ourselves and that's it.
3062  // Actually $cachedRow contains content that we could show instead of rendering. Maybe we should do that to gain more performance but then we should set all the stuff done in $this->getFromCache()... For now we stick to this...
3063  $this->set_no_cache('Another process wrote into the cache since the beginning of the render process', true);
3064 
3065  // Since the new Locking API this should never be the case
3066  } else {
3067  $this->tempContent = true;
3068  // This flag shows that temporary content is put in the cache
3069  $this->setPageCacheContent($temp_content, $this->config, $GLOBALS['EXEC_TIME'] + $seconds);
3070  }
3071  }
3072  }
3073 
3079  public function realPageCacheContent()
3080  {
3081  // seconds until a cached page is too old
3082  $cacheTimeout = $this->get_cache_timeout();
3083  $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
3084  $this->tempContent = false;
3085  $usePageCache = true;
3086  // Hook for deciding whether page cache should be written to the cache backend or not
3087  // NOTE: as hooks are called in a loop, the last hook will have the final word (however each
3088  // hook receives the current status of the $usePageCache flag)
3089  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache'])) {
3090  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache'] as $_classRef) {
3091  $_procObj = GeneralUtility::getUserObj($_classRef);
3092  $usePageCache = $_procObj->usePageCache($this, $usePageCache);
3093  }
3094  }
3095  // Write the page to cache, if necessary
3096  if ($usePageCache) {
3097  $this->setPageCacheContent($this->content, $this->config, $timeOutTime);
3098  }
3099  // Hook for cache post processing (eg. writing static files!)
3100  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache'])) {
3101  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache'] as $_classRef) {
3102  $_procObj = GeneralUtility::getUserObj($_classRef);
3103  $_procObj->insertPageIncache($this, $timeOutTime);
3104  }
3105  }
3106  }
3107 
3117  public function setPageCacheContent($content, $data, $expirationTstamp)
3118  {
3119  $cacheData = [
3120  'identifier' => $this->newHash,
3121  'page_id' => $this->id,
3122  'content' => $content,
3123  'temp_content' => $this->tempContent,
3124  'cache_data' => $data,
3125  'expires' => $expirationTstamp,
3126  'tstamp' => $GLOBALS['EXEC_TIME'],
3127  'pageTitleInfo' => [
3128  'title' => $this->page['title'],
3129  'altPageTitle' => $this->altPageTitle,
3130  'indexedDocTitle' => $this->indexedDocTitle
3131  ]
3132  ];
3133  $this->cacheExpires = $expirationTstamp;
3134  $this->pageCacheTags[] = 'pageId_' . $cacheData['page_id'];
3135  if ($this->page_cache_reg1) {
3136  $reg1 = (int)$this->page_cache_reg1;
3137  $cacheData['reg1'] = $reg1;
3138  $this->pageCacheTags[] = 'reg1_' . $reg1;
3139  }
3140  if (!empty($this->page['cache_tags'])) {
3141  $tags = GeneralUtility::trimExplode(',', $this->page['cache_tags'], true);
3142  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
3143  }
3144  $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - $GLOBALS['EXEC_TIME']);
3145  }
3146 
3152  public function clearPageCacheContent()
3153  {
3154  $this->pageCache->remove($this->newHash);
3155  }
3156 
3163  public function clearPageCacheContent_pidList($pidList)
3164  {
3165  $pageIds = GeneralUtility::trimExplode(',', $pidList);
3166  foreach ($pageIds as $pageId) {
3167  $this->pageCache->flushByTag('pageId_' . (int)$pageId);
3168  }
3169  }
3170 
3178  public function setSysLastChanged()
3179  {
3180  // Draft workspaces are always uid 1 or more. We do not update SYS_LASTCHANGED if we are browsing page from one of theses workspaces
3181  if ((int)$this->whichWorkspace() < 1 && $this->page['SYS_LASTCHANGED'] < (int)$this->register['SYS_LASTCHANGED']) {
3182  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
3183  ->getConnectionForTable('pages');
3184  $connection->update(
3185  'pages',
3186  [
3187  'SYS_LASTCHANGED' => (int)$this->register['SYS_LASTCHANGED']
3188  ],
3189  [
3190  'uid' => (int)$this->id
3191  ]
3192  );
3193  }
3194  }
3195 
3202  public function releaseLocks()
3203  {
3204  $this->releaseLock('pagesection');
3205  $this->releaseLock('pages');
3206  }
3207 
3215  public function addCacheTags(array $tags)
3216  {
3217  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
3218  }
3219 
3220  /********************************************
3221  *
3222  * Page generation; rendering and inclusion
3223  *
3224  *******************************************/
3230  public function generatePage_preProcessing()
3231  {
3232  // Same codeline as in getFromCache(). But $this->all has been changed by
3233  // \TYPO3\CMS\Core\TypoScript\TemplateService::start() in the meantime, so this must be called again!
3234  $this->newHash = $this->getHash();
3235 
3236  // If the pages_lock is set, we are in charge of generating the page.
3237  if (is_object($this->locks['pages']['accessLock'])) {
3238  // Here we put some temporary stuff in the cache in order to let the first hit generate the page.
3239  // The temporary cache will expire after a few seconds (typ. 30) or will be cleared by the rendered page,
3240  // which will also clear and rewrite the cache.
3241  $this->tempPageCacheContent();
3242  }
3243  // At this point we have a valid pagesection_cache and also some temporary page_cache content,
3244  // so let all other processes proceed now. (They are blocked at the pagessection_lock in getFromCache())
3245  $this->releaseLock('pagesection');
3246 
3247  // Setting cache_timeout_default. May be overridden by PHP include scripts.
3248  $this->cacheTimeOutDefault = (int)$this->config['config']['cache_period'];
3249  // Page is generated
3250  $this->no_cacheBeforePageGen = $this->no_cache;
3251  }
3252 
3259  public function generatePage_whichScript()
3260  {
3261  if (!$GLOBALS['TYPO3_CONF_VARS']['FE']['noPHPscriptInclude'] && $this->config['config']['pageGenScript']) {
3262  return $this->tmpl->getFileName($this->config['config']['pageGenScript']);
3263  }
3264  return null;
3265  }
3266 
3274  {
3275  // This is to ensure, that the page is NOT cached if the no_cache parameter was set before the page was generated. This is a safety precaution, as it could have been unset by some script.
3276  if ($this->no_cacheBeforePageGen) {
3277  $this->set_no_cache('no_cache has been set before the page was generated - safety check', true);
3278  }
3279  // Hook for post-processing of page content cached/non-cached:
3280  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'])) {
3281  $_params = ['pObj' => &$this];
3282  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'] as $_funcRef) {
3283  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3284  }
3285  }
3286  // Processing if caching is enabled:
3287  if (!$this->no_cache) {
3288  // Hook for post-processing of page content before being cached:
3289  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached'])) {
3290  $_params = ['pObj' => &$this];
3291  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached'] as $_funcRef) {
3292  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3293  }
3294  }
3295  }
3296  // Convert char-set for output: (should be BEFORE indexing of the content (changed 22/4 2005)),
3297  // because otherwise indexed search might convert from the wrong charset!
3298  // One thing is that the charset mentioned in the HTML header would be wrong since the output charset (metaCharset)
3299  // has not been converted to from utf-8. And indexed search will internally convert from metaCharset
3300  // to utf-8 so the content MUST be in metaCharset already!
3301  $this->content = $this->convOutputCharset($this->content);
3302  // Hook for indexing pages
3303  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageIndexing'])) {
3304  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageIndexing'] as $_classRef) {
3305  $_procObj = GeneralUtility::getUserObj($_classRef);
3306  $_procObj->hook_indexContent($this);
3307  }
3308  }
3309  // Storing for cache:
3310  if (!$this->no_cache) {
3311  $this->realPageCacheContent();
3312  } elseif ($this->tempContent) {
3313  // If there happens to be temporary content in the cache and the cache was not cleared due to new content, put it in... ($this->no_cache=0)
3314  $this->clearPageCacheContent();
3315  $this->tempContent = false;
3316  }
3317  // Sets sys-last-change:
3318  $this->setSysLastChanged();
3319  }
3320 
3326  protected function regeneratePageTitle()
3327  {
3328  PageGenerator::generatePageTitle();
3329  }
3330 
3336  public function INTincScript()
3337  {
3338  // Deprecated stuff:
3339  // @deprecated: annotation added TYPO3 4.6
3340  $this->additionalHeaderData = is_array($this->config['INTincScript_ext']['additionalHeaderData']) ? $this->config['INTincScript_ext']['additionalHeaderData'] : [];
3341  $this->additionalFooterData = is_array($this->config['INTincScript_ext']['additionalFooterData']) ? $this->config['INTincScript_ext']['additionalFooterData'] : [];
3342  $this->additionalJavaScript = $this->config['INTincScript_ext']['additionalJavaScript'];
3343  $this->additionalCSS = $this->config['INTincScript_ext']['additionalCSS'];
3344  $this->divSection = '';
3345  if (empty($this->config['INTincScript_ext']['pageRenderer'])) {
3346  $this->initPageRenderer();
3347  } else {
3349  $pageRenderer = unserialize($this->config['INTincScript_ext']['pageRenderer']);
3350  $this->pageRenderer = $pageRenderer;
3352  }
3353 
3355  $this->getTimeTracker()->push('Substitute header section');
3356  $this->INTincScript_loadJSCode();
3357  $this->regeneratePageTitle();
3358 
3359  $this->content = str_replace(
3360  [
3361  '<!--HD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
3362  '<!--FD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
3363  '<!--TDS_' . $this->config['INTincScript_ext']['divKey'] . '-->'
3364  ],
3365  [
3366  $this->convOutputCharset(implode(LF, $this->additionalHeaderData)),
3367  $this->convOutputCharset(implode(LF, $this->additionalFooterData)),
3368  $this->convOutputCharset($this->divSection),
3369  ],
3370  $this->pageRenderer->renderJavaScriptAndCssForProcessingOfUncachedContentObjects($this->content, $this->config['INTincScript_ext']['divKey'])
3371  );
3372  // Replace again, because header and footer data and page renderer replacements may introduce additional placeholders (see #44825)
3374  $this->setAbsRefPrefix();
3375  $this->getTimeTracker()->pull();
3376  }
3377 
3384  {
3385  do {
3386  $INTiS_config = $this->config['INTincScript'];
3387  $this->INTincScript_process($INTiS_config);
3388  // Check if there were new items added to INTincScript during the previous execution:
3389  $INTiS_config = array_diff_assoc($this->config['INTincScript'], $INTiS_config);
3390  $reprocess = count($INTiS_config) > 0;
3391  } while ($reprocess);
3392  }
3393 
3401  protected function INTincScript_process($INTiS_config)
3402  {
3403  $timeTracker = $this->getTimeTracker();
3404  $timeTracker->push('Split content');
3405  // Splits content with the key.
3406  $INTiS_splitC = explode('<!--INT_SCRIPT.', $this->content);
3407  $this->content = '';
3408  $timeTracker->setTSlogMessage('Parts: ' . count($INTiS_splitC));
3409  $timeTracker->pull();
3410  foreach ($INTiS_splitC as $INTiS_c => $INTiS_cPart) {
3411  // If the split had a comment-end after 32 characters it's probably a split-string
3412  if (substr($INTiS_cPart, 32, 3) === '-->') {
3413  $INTiS_key = 'INT_SCRIPT.' . substr($INTiS_cPart, 0, 32);
3414  if (is_array($INTiS_config[$INTiS_key])) {
3415  $label = 'Include ' . $INTiS_config[$INTiS_key]['type'];
3416  $label = $label . isset($INTiS_config[$INTiS_key]['file']) ? ' ' . $INTiS_config[$INTiS_key]['file'] : '';
3417  $timeTracker->push($label, '');
3418  $incContent = '';
3419  $INTiS_cObj = unserialize($INTiS_config[$INTiS_key]['cObj']);
3420  /* @var $INTiS_cObj ContentObjectRenderer */
3421  switch ($INTiS_config[$INTiS_key]['type']) {
3422  case 'COA':
3423  $incContent = $INTiS_cObj->cObjGetSingle('COA', $INTiS_config[$INTiS_key]['conf']);
3424  break;
3425  case 'FUNC':
3426  $incContent = $INTiS_cObj->cObjGetSingle('USER', $INTiS_config[$INTiS_key]['conf']);
3427  break;
3428  case 'POSTUSERFUNC':
3429  $incContent = $INTiS_cObj->callUserFunction($INTiS_config[$INTiS_key]['postUserFunc'], $INTiS_config[$INTiS_key]['conf'], $INTiS_config[$INTiS_key]['content']);
3430  break;
3431  }
3432  $this->content .= $this->convOutputCharset($incContent);
3433  $this->content .= substr($INTiS_cPart, 35);
3434  $timeTracker->pull($incContent);
3435  } else {
3436  $this->content .= substr($INTiS_cPart, 35);
3437  }
3438  } else {
3439  $this->content .= ($INTiS_c ? '<!--INT_SCRIPT.' : '') . $INTiS_cPart;
3440  }
3441  }
3442  }
3443 
3449  public function INTincScript_loadJSCode()
3450  {
3451  // Add javascript
3452  $jsCode = trim($this->JSCode);
3453  $additionalJavaScript = is_array($this->additionalJavaScript)
3454  ? implode(LF, $this->additionalJavaScript)
3457  if ($jsCode !== '' || $additionalJavaScript !== '') {
3458  $this->additionalHeaderData['JSCode'] = '
3459 <script type="text/javascript">
3460  /*<![CDATA[*/
3461 <!--
3462 ' . $additionalJavaScript . '
3463 ' . $jsCode . '
3464 // -->
3465  /*]]>*/
3466 </script>';
3467  }
3468  // Add CSS
3469  $additionalCss = is_array($this->additionalCSS) ? implode(LF, $this->additionalCSS) : $this->additionalCSS;
3470  $additionalCss = trim($additionalCss);
3471  if ($additionalCss !== '') {
3472  $this->additionalHeaderData['_CSS'] = '
3473 <style type="text/css">
3474 ' . $additionalCss . '
3475 </style>';
3476  }
3477  }
3478 
3484  public function isINTincScript()
3485  {
3486  return is_array($this->config['INTincScript']) && empty($this->activeUrlHandlers);
3487  }
3488 
3489  /********************************************
3490  *
3491  * Finished off; outputting, storing session data, statistics...
3492  *
3493  *******************************************/
3500  public function isOutputting()
3501  {
3502  // Initialize by status if there is a Redirect URL
3503  $enableOutput = empty($this->activeUrlHandlers);
3504  // Call hook for possible disabling of output:
3505  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting'])) {
3506  $_params = ['pObj' => &$this, 'enableOutput' => &$enableOutput];
3507  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting'] as $_funcRef) {
3508  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3509  }
3510  }
3511  return $enableOutput;
3512  }
3513 
3523  public function processOutput()
3524  {
3525  // Set header for charset-encoding unless disabled
3526  if (empty($this->config['config']['disableCharsetHeader'])) {
3527  $headLine = 'Content-Type: ' . $this->contentType . '; charset=' . trim($this->metaCharset);
3528  header($headLine);
3529  }
3530  // Set header for content language unless disabled
3531  if (empty($this->config['config']['disableLanguageHeader']) && !empty($this->sys_language_isocode)) {
3532  $headLine = 'Content-Language: ' . trim($this->sys_language_isocode);
3533  header($headLine);
3534  }
3535  // Set cache related headers to client (used to enable proxy / client caching!)
3536  if (!empty($this->config['config']['sendCacheHeaders'])) {
3537  $this->sendCacheHeaders();
3538  }
3539  // Set headers, if any
3540  if (is_array($this->config['config']['additionalHeaders.'])) {
3541  ksort($this->config['config']['additionalHeaders.']);
3542  foreach ($this->config['config']['additionalHeaders.'] as $options) {
3543  header(
3544  trim($options['header']),
3545  // "replace existing headers" is turned on by default, unless turned off
3546  ($options['replace'] !== '0'),
3547  ((int)$options['httpResponseCode'] ?: null)
3548  );
3549  }
3550  }
3551  // Send appropriate status code in case of temporary content
3552  if ($this->tempContent) {
3553  $this->addTempContentHttpHeaders();
3554  }
3555  // Make substitution of eg. username/uid in content only if cache-headers for client/proxy caching is NOT sent!
3556  if (!$this->isClientCachable) {
3557  $this->contentStrReplace();
3558  }
3559  // Hook for post-processing of page content before output:
3560  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output'])) {
3561  $_params = ['pObj' => &$this];
3562  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output'] as $_funcRef) {
3563  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3564  }
3565  }
3566  // Send content-length header.
3567  // Notice that all HTML content outside the length of the content-length header will be cut off!
3568  // Therefore content of unknown length from included PHP-scripts and if admin users are logged
3569  // in (admin panel might show...) or if debug mode is turned on, we disable it!
3570  if (
3571  (!isset($this->config['config']['enableContentLengthHeader']) || $this->config['config']['enableContentLengthHeader'])
3572  && !$this->beUserLogin && !$GLOBALS['TYPO3_CONF_VARS']['FE']['debug']
3573  && !$this->config['config']['debug'] && !$this->doWorkspacePreview()
3574  ) {
3575  header('Content-Length: ' . strlen($this->content));
3576  }
3577  }
3578 
3586  public function sendCacheHeaders()
3587  {
3588  // Getting status whether we can send cache control headers for proxy caching:
3589  $doCache = $this->isStaticCacheble();
3590  // This variable will be TRUE unless cache headers are configured to be sent ONLY if a branch does not allow logins and logins turns out to be allowed anyway...
3591  $loginsDeniedCfg = empty($this->config['config']['sendCacheHeaders_onlyWhenLoginDeniedInBranch']) || empty($this->loginAllowedInBranch);
3592  // Finally, when backend users are logged in, do not send cache headers at all (Admin Panel might be displayed for instance).
3593  if ($doCache && !$this->beUserLogin && !$this->doWorkspacePreview() && $loginsDeniedCfg) {
3594  // Build headers:
3595  $headers = [
3596  'Expires: ' . gmdate('D, d M Y H:i:s T', $this->cacheExpires),
3597  'ETag: "' . md5($this->content) . '"',
3598  'Cache-Control: max-age=' . ($this->cacheExpires - $GLOBALS['EXEC_TIME']),
3599  // no-cache
3600  'Pragma: public'
3601  ];
3602  $this->isClientCachable = true;
3603  } else {
3604  // Build headers:
3605  $headers = [
3606  'Cache-Control: private'
3607  ];
3608  $this->isClientCachable = false;
3609  // Now, if a backend user is logged in, tell him in the Admin Panel log what the caching status would have been:
3610  if ($this->beUserLogin) {
3611  if ($doCache) {
3612  $this->getTimeTracker()->setTSlogMessage('Cache-headers with max-age "' . ($this->cacheExpires - $GLOBALS['EXEC_TIME']) . '" would have been sent');
3613  } else {
3614  $reasonMsg = '';
3615  $reasonMsg .= !$this->no_cache ? '' : 'Caching disabled (no_cache). ';
3616  $reasonMsg .= !$this->isINTincScript() ? '' : '*_INT object(s) on page. ';
3617  $reasonMsg .= !is_array($this->fe_user->user) ? '' : 'Frontend user logged in. ';
3618  $this->getTimeTracker()->setTSlogMessage('Cache-headers would disable proxy caching! Reason(s): "' . $reasonMsg . '"', 1);
3619  }
3620  }
3621  }
3622  // Send headers:
3623  foreach ($headers as $hL) {
3624  header($hL);
3625  }
3626  }
3627 
3638  public function isStaticCacheble()
3639  {
3640  $doCache = !$this->no_cache && !$this->isINTincScript() && !$this->isUserOrGroupSet();
3641  return $doCache;
3642  }
3643 
3649  public function contentStrReplace()
3650  {
3651  $search = [];
3652  $replace = [];
3653  // Substitutes username mark with the username
3654  if (!empty($this->fe_user->user['uid'])) {
3655  // User name:
3656  $token = isset($this->config['config']['USERNAME_substToken']) ? trim($this->config['config']['USERNAME_substToken']) : '';
3657  $search[] = $token ? $token : '<!--###USERNAME###-->';
3658  $replace[] = $this->fe_user->user['username'];
3659  // User uid (if configured):
3660  $token = isset($this->config['config']['USERUID_substToken']) ? trim($this->config['config']['USERUID_substToken']) : '';
3661  if ($token) {
3662  $search[] = $token;
3663  $replace[] = $this->fe_user->user['uid'];
3664  }
3665  }
3666  // Substitutes get_URL_ID in case of GET-fallback
3667  if ($this->getMethodUrlIdToken) {
3668  $search[] = $this->getMethodUrlIdToken;
3669  $replace[] = $this->fe_user->get_URL_ID;
3670  }
3671  // Hook for supplying custom search/replace data
3672  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-contentStrReplace'])) {
3673  $contentStrReplaceHooks = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-contentStrReplace'];
3674  if (is_array($contentStrReplaceHooks)) {
3675  $_params = [
3676  'search' => &$search,
3677  'replace' => &$replace
3678  ];
3679  foreach ($contentStrReplaceHooks as $_funcRef) {
3680  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3681  }
3682  }
3683  }
3684  if (!empty($search)) {
3685  $this->content = str_replace($search, $replace, $this->content);
3686  }
3687  }
3688 
3694  public function storeSessionData()
3695  {
3696  $this->fe_user->storeSessionData();
3697  }
3698 
3705  public function setParseTime()
3706  {
3707  // Compensates for the time consumed with Back end user initialization.
3708  $microtime_start = isset($GLOBALS['TYPO3_MISC']['microtime_start']) ? $GLOBALS['TYPO3_MISC']['microtime_start'] : null;
3709  $microtime_end = isset($GLOBALS['TYPO3_MISC']['microtime_end']) ? $GLOBALS['TYPO3_MISC']['microtime_end'] : null;
3710  $microtime_BE_USER_start = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_start']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] : null;
3711  $microtime_BE_USER_end = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_end']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] : null;
3712  $timeTracker = $this->getTimeTracker();
3713  $this->scriptParseTime = $timeTracker->getMilliseconds($microtime_end) - $timeTracker->getMilliseconds($microtime_start) - ($timeTracker->getMilliseconds($microtime_BE_USER_end) - $timeTracker->getMilliseconds($microtime_BE_USER_start));
3714  }
3715 
3721  public function previewInfo()
3722  {
3723  if ($this->fePreview !== 0) {
3724  $previewInfo = '';
3725  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo'])) {
3726  $_params = ['pObj' => &$this];
3727  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo'] as $_funcRef) {
3728  $previewInfo .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3729  }
3730  }
3731  $this->content = str_ireplace('</body>', $previewInfo . '</body>', $this->content);
3732  }
3733  }
3734 
3740  public function hook_eofe()
3741  {
3742  // Call hook for end-of-frontend processing:
3743  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe'])) {
3744  $_params = ['pObj' => &$this];
3745  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe'] as $_funcRef) {
3746  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3747  }
3748  }
3749  }
3750 
3756  public function beLoginLinkIPList()
3757  {
3758  if (!empty($this->config['config']['beLoginLinkIPList'])) {
3759  if (GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $this->config['config']['beLoginLinkIPList'])) {
3760  $label = !$this->beUserLogin ? $this->config['config']['beLoginLinkIPList_login'] : $this->config['config']['beLoginLinkIPList_logout'];
3761  if ($label) {
3762  if (!$this->beUserLogin) {
3763  $link = '<a href="' . htmlspecialchars((TYPO3_mainDir . 'index.php?redirect_url=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')))) . '">' . $label . '</a>';
3764  } else {
3765  $link = '<a href="' . htmlspecialchars((TYPO3_mainDir . 'index.php?L=OUT&redirect_url=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')))) . '">' . $label . '</a>';
3766  }
3767  return $link;
3768  }
3769  }
3770  }
3771  return '';
3772  }
3773 
3779  public function addTempContentHttpHeaders()
3780  {
3781  header('HTTP/1.0 503 Service unavailable');
3782  header('Retry-after: 3600');
3783  header('Pragma: no-cache');
3784  header('Cache-control: no-cache');
3785  header('Expire: 0');
3786  }
3787 
3788  /********************************************
3789  *
3790  * Various internal API functions
3791  *
3792  *******************************************/
3803  public function encryptCharcode($n, $start, $end, $offset)
3804  {
3805  $n = $n + $offset;
3806  if ($offset > 0 && $n > $end) {
3807  $n = $start + ($n - $end - 1);
3808  } elseif ($offset < 0 && $n < $start) {
3809  $n = $end - ($start - $n - 1);
3810  }
3811  return chr($n);
3812  }
3813 
3821  public function encryptEmail($string, $back = false)
3822  {
3823  $out = '';
3824  // obfuscates using the decimal HTML entity references for each character
3825  if ($this->spamProtectEmailAddresses === 'ascii') {
3826  $stringLength = strlen($string);
3827  for ($a = 0; $a < $stringLength; $a++) {
3828  $out .= '&#' . ord(substr($string, $a, 1)) . ';';
3829  }
3830  } else {
3831  // like str_rot13() but with a variable offset and a wider character range
3832  $len = strlen($string);
3833  $offset = (int)$this->spamProtectEmailAddresses * ($back ? -1 : 1);
3834  for ($i = 0; $i < $len; $i++) {
3835  $charValue = ord($string[$i]);
3836  // 0-9 . , - + / :
3837  if ($charValue >= 43 && $charValue <= 58) {
3838  $out .= $this->encryptCharcode($charValue, 43, 58, $offset);
3839  } elseif ($charValue >= 64 && $charValue <= 90) {
3840  // A-Z @
3841  $out .= $this->encryptCharcode($charValue, 64, 90, $offset);
3842  } elseif ($charValue >= 97 && $charValue <= 122) {
3843  // a-z
3844  $out .= $this->encryptCharcode($charValue, 97, 122, $offset);
3845  } else {
3846  $out .= $string[$i];
3847  }
3848  }
3849  }
3850  return $out;
3851  }
3852 
3860  public function newCObj()
3861  {
3862  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
3863  $this->cObj->start($this->page, 'pages');
3864  }
3865 
3873  public function setAbsRefPrefix()
3874  {
3875  if (!$this->absRefPrefix) {
3876  return;
3877  }
3878  $search = [
3879  '"typo3temp/',
3880  '"typo3conf/ext/',
3881  '"' . TYPO3_mainDir . 'ext/',
3882  '"' . TYPO3_mainDir . 'sysext/'
3883  ];
3884  $replace = [
3885  '"' . $this->absRefPrefix . 'typo3temp/',
3886  '"' . $this->absRefPrefix . 'typo3conf/ext/',
3887  '"' . $this->absRefPrefix . TYPO3_mainDir . 'ext/',
3888  '"' . $this->absRefPrefix . TYPO3_mainDir . 'sysext/'
3889  ];
3891  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
3892  $storages = $storageRepository->findAll();
3893  foreach ($storages as $storage) {
3894  if ($storage->getDriverType() === 'Local' && $storage->isPublic() && $storage->isOnline()) {
3895  $folder = $storage->getPublicUrl($storage->getRootLevelFolder(), true);
3896  $search[] = '"' . $folder;
3897  $replace[] = '"' . $this->absRefPrefix . $folder;
3898  }
3899  }
3900  // Process additional directories
3901  $directories = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'], true);
3902  foreach ($directories as $directory) {
3903  $search[] = '"' . $directory;
3904  $replace[] = '"' . $this->absRefPrefix . $directory;
3905  }
3906  $this->content = str_replace(
3907  $search,
3908  $replace,
3909  $this->content
3910  );
3911  }
3912 
3920  public function baseUrlWrap($url)
3921  {
3922  if ($this->baseUrl) {
3923  $urlParts = parse_url($url);
3924  if (empty($urlParts['scheme']) && $url[0] !== '/') {
3925  $url = $this->baseUrl . $url;
3926  }
3927  }
3928  return $url;
3929  }
3930 
3940  public function logDeprecatedTyposcript($typoScriptProperty, $explanation = '')
3941  {
3942  $explanationText = $explanation !== '' ? ' - ' . $explanation : '';
3943  $this->getTimeTracker()->setTSlogMessage($typoScriptProperty . ' is deprecated.' . $explanationText, 2);
3944  GeneralUtility::deprecationLog('TypoScript ' . $typoScriptProperty . ' is deprecated' . $explanationText);
3945  }
3946 
3954  public function updateMD5paramsRecord($hash)
3955  {
3956  $connection = GeneralUtility::makeInstance(ConnectionPool::class)
3957  ->getConnectionForTable('cache_md5params');
3958  $connection->update(
3959  'cache_md5params',
3960  [
3961  'tstamp' => $GLOBALS['EXEC_TIME']
3962  ],
3963  [
3964  'md5hash' => $hash
3965  ]
3966  );
3967  }
3968 
3969  /********************************************
3970  * PUBLIC ACCESSIBLE WORKSPACES FUNCTIONS
3971  *******************************************/
3972 
3978  public function doWorkspacePreview()
3979  {
3980  return $this->workspacePreview !== 0;
3981  }
3982 
3989  public function whichWorkspace($returnTitle = false)
3990  {
3991  $ws = null;
3992  if ($this->doWorkspacePreview()) {
3993  $ws = (int)$this->workspacePreview;
3994  } elseif ($this->beUserLogin) {
3995  $ws = $this->getBackendUser()->workspace;
3996  }
3997  if ($ws && $returnTitle) {
3998  if (ExtensionManagementUtility::isLoaded('workspaces')) {
3999  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
4000  ->getQueryBuilderForTable('sys_workspace');
4001 
4002  $queryBuilder->getRestrictions()->removeAll();
4003 
4004  $row = $queryBuilder
4005  ->select('title')
4006  ->from('sys_workspace')
4007  ->where(
4008  $queryBuilder->expr()->eq(
4009  'uid',
4010  $queryBuilder->createNamedParameter($ws, \PDO::PARAM_INT)
4011  )
4012  )
4013  ->execute()
4014  ->fetch();
4015 
4016  if ($row) {
4017  return $row['title'];
4018  }
4019  }
4020  }
4021  return $ws;
4022  }
4023 
4031  public function includeLibraries(array $libraries)
4032  {
4034  $timeTracker = $this->getTimeTracker();
4035  $timeTracker->push('Include libraries');
4036  $timeTracker->setTSlogMessage('Files for inclusion: "' . implode(', ', $libraries) . '"');
4037  foreach ($libraries as $library) {
4038  $file = $this->tmpl->getFileName($library);
4039  if ($file) {
4040  include_once './' . $file;
4041  } else {
4042  $timeTracker->setTSlogMessage('Include file "' . $file . '" did not exist!', 2);
4043  }
4044  }
4045  $timeTracker->pull();
4046  }
4047 
4048  /********************************************
4049  *
4050  * Various external API functions - for use in plugins etc.
4051  *
4052  *******************************************/
4053 
4059  public function getPagesTSconfig()
4060  {
4061  if (!is_array($this->pagesTSconfig)) {
4062  $TSdataArray = [];
4063  foreach ($this->rootLine as $k => $v) {
4064  $TSdataArray[] = $v['TSconfig'];
4065  }
4066  // Adding the default configuration:
4067  $TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'];
4068  // Bring everything in the right order. Default first, then the Rootline down to the current page
4069  $TSdataArray = array_reverse($TSdataArray);
4070  // Parsing the user TS (or getting from cache)
4071  $TSdataArray = TypoScriptParser::checkIncludeLines_array($TSdataArray);
4072  $userTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
4073  $hash = md5('pageTS:' . $userTS);
4074  $cachedContent = $this->sys_page->getHash($hash);
4075  if (is_array($cachedContent)) {
4076  $this->pagesTSconfig = $cachedContent;
4077  } else {
4078  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
4079  $parseObj->parse($userTS);
4080  $this->pagesTSconfig = $parseObj->setup;
4081  $this->sys_page->storeHash($hash, $this->pagesTSconfig, 'PAGES_TSconfig');
4082  }
4083  }
4084  return $this->pagesTSconfig;
4085  }
4086 
4095  public function setJS($key, $content = '')
4096  {
4097  if ($key) {
4098  switch ($key) {
4099  case 'mouseOver':
4100  $this->additionalJavaScript[$key] = ' // JS function for mouse-over
4101  function over(name, imgObj) { //
4102  if (document[name]) {document[name].src = eval(name+"_h.src");}
4103  else if (document.getElementById && document.getElementById(name)) {document.getElementById(name).src = eval(name+"_h.src");}
4104  else if (imgObj) {imgObj.src = eval(name+"_h.src");}
4105  }
4106  // JS function for mouse-out
4107  function out(name, imgObj) { //
4108  if (document[name]) {document[name].src = eval(name+"_n.src");}
4109  else if (document.getElementById && document.getElementById(name)) {document.getElementById(name).src = eval(name+"_n.src");}
4110  else if (imgObj) {imgObj.src = eval(name+"_n.src");}
4111  }';
4112  break;
4113  case 'openPic':
4114  $this->additionalJavaScript[$key] = ' function openPic(url, winName, winParams) { //
4115  var theWindow = window.open(url, winName, winParams);
4116  if (theWindow) {theWindow.focus();}
4117  }';
4118  break;
4119  default:
4120  $this->additionalJavaScript[$key] = $content;
4121  }
4122  }
4123  }
4124 
4133  public function setCSS($key, $content)
4134  {
4135  if ($key) {
4136  $this->additionalCSS[$key] = $content;
4137  }
4138  }
4139 
4147  public function uniqueHash($str = '')
4148  {
4149  return md5($this->uniqueString . '_' . $str . $this->uniqueCounter++);
4150  }
4151 
4159  public function set_no_cache($reason = '', $internal = false)
4160  {
4161  if ($internal && isset($GLOBALS['BE_USER'])) {
4163  } else {
4165  }
4166 
4167  if ($reason !== '') {
4168  $warning = '$TSFE->set_no_cache() was triggered. Reason: ' . $reason . '.';
4169  } else {
4170  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
4171  // This is a hack to work around ___FILE___ resolving symbolic links
4172  $PATH_site_real = dirname(realpath(PATH_site . 'typo3')) . '/';
4173  $file = $trace[0]['file'];
4174  if (strpos($file, $PATH_site_real) === 0) {
4175  $file = str_replace($PATH_site_real, '', $file);
4176  } else {
4177  $file = str_replace(PATH_site, '', $file);
4178  }
4179  $line = $trace[0]['line'];
4180  $trigger = $file . ' on line ' . $line;
4181  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by ' . $trigger . '.';
4182  }
4183  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) {
4184  $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!';
4185  $this->getTimeTracker()->setTSlogMessage($warning, 2);
4186  } else {
4187  $warning .= ' Caching is disabled!';
4188  $this->disableCache();
4189  }
4190  GeneralUtility::sysLog($warning, 'cms', $severity);
4191  }
4192 
4199  protected function disableCache()
4200  {
4201  $this->no_cache = true;
4202  }
4203 
4210  public function set_cache_timeout_default($seconds)
4211  {
4212  $this->cacheTimeOutDefault = (int)$seconds;
4213  }
4214 
4220  public function get_cache_timeout()
4221  {
4223  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4224  $cachedCacheLifetimeIdentifier = 'core-tslib_fe-get_cache_timeout';
4225  $cachedCacheLifetime = $runtimeCache->get($cachedCacheLifetimeIdentifier);
4226  if ($cachedCacheLifetime === false) {
4227  if ($this->page['cache_timeout']) {
4228  // Cache period was set for the page:
4229  $cacheTimeout = $this->page['cache_timeout'];
4230  } elseif ($this->cacheTimeOutDefault) {
4231  // Cache period was set for the whole site:
4232  $cacheTimeout = $this->cacheTimeOutDefault;
4233  } else {
4234  // No cache period set at all, so we take one day (60*60*24 seconds = 86400 seconds):
4235  $cacheTimeout = 86400;
4236  }
4237  if ($this->config['config']['cache_clearAtMidnight']) {
4238  $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
4239  $midnightTime = mktime(0, 0, 0, date('m', $timeOutTime), date('d', $timeOutTime), date('Y', $timeOutTime));
4240  // If the midnight time of the expire-day is greater than the current time,
4241  // we may set the timeOutTime to the new midnighttime.
4242  if ($midnightTime > $GLOBALS['EXEC_TIME']) {
4243  $cacheTimeout = $midnightTime - $GLOBALS['EXEC_TIME'];
4244  }
4245  }
4246 
4247  // Calculate the timeout time for records on the page and adjust cache timeout if necessary
4248  $cacheTimeout = min($this->calculatePageCacheTimeout(), $cacheTimeout);
4249 
4250  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['get_cache_timeout'])) {
4251  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['get_cache_timeout'] as $_funcRef) {
4252  $params = ['cacheTimeout' => $cacheTimeout];
4253  $cacheTimeout = GeneralUtility::callUserFunction($_funcRef, $params, $this);
4254  }
4255  }
4256  $runtimeCache->set($cachedCacheLifetimeIdentifier, $cacheTimeout);
4257  $cachedCacheLifetime = $cacheTimeout;
4258  }
4259  return $cachedCacheLifetime;
4260  }
4261 
4268  public function getUniqueId($desired = '')
4269  {
4270  if ($desired === '') {
4271  // id has to start with a letter to reach XHTML compliance
4272  $uniqueId = 'a' . $this->uniqueHash();
4273  } else {
4274  $uniqueId = $desired;
4275  for ($i = 1; isset($this->usedUniqueIds[$uniqueId]); $i++) {
4276  $uniqueId = $desired . '_' . $i;
4277  }
4278  }
4279  $this->usedUniqueIds[$uniqueId] = true;
4280  return $uniqueId;
4281  }
4282 
4283  /*********************************************
4284  *
4285  * Localization and character set conversion
4286  *
4287  *********************************************/
4294  public function sL($input)
4295  {
4296  if (substr($input, 0, 4) !== 'LLL:') {
4297  // Not a label, return the key as this
4298  return $input;
4299  }
4300  // If cached label
4301  if (!isset($this->LL_labels_cache[$this->lang][$input])) {
4302  $restStr = trim(substr($input, 4));
4303  $extPrfx = '';
4304  if (strpos($restStr, 'EXT:') === 0) {
4305  $restStr = trim(substr($restStr, 4));
4306  $extPrfx = 'EXT:';
4307  }
4308  $parts = explode(':', $restStr);
4309  $parts[0] = $extPrfx . $parts[0];
4310  // Getting data if not cached
4311  if (!isset($this->LL_files_cache[$parts[0]])) {
4312  $this->LL_files_cache[$parts[0]] = $this->readLLfile($parts[0]);
4313  }
4314  $this->LL_labels_cache[$this->lang][$input] = $this->getLLL($parts[1], $this->LL_files_cache[$parts[0]]);
4315  }
4316  return $this->LL_labels_cache[$this->lang][$input];
4317  }
4318 
4325  public function readLLfile($fileRef)
4326  {
4328  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
4329 
4330  if ($this->lang !== 'default') {
4331  $languages = array_reverse($this->languageDependencies);
4332  // At least we need to have English
4333  if (empty($languages)) {
4334  $languages[] = 'default';
4335  }
4336  } else {
4337  $languages = ['default'];
4338  }
4339 
4340  $localLanguage = [];
4341  foreach ($languages as $language) {
4342  $tempLL = $languageFactory->getParsedData($fileRef, $language, 'utf-8');
4343  $localLanguage['default'] = $tempLL['default'];
4344  if (!isset($localLanguage[$this->lang])) {
4345  $localLanguage[$this->lang] = $localLanguage['default'];
4346  }
4347  if ($this->lang !== 'default' && isset($tempLL[$language])) {
4348  // Merge current language labels onto labels from previous language
4349  // This way we have a label with fall back applied
4350  ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
4351  }
4352  }
4353 
4354  return $localLanguage;
4355  }
4356 
4364  public function getLLL($index, $LOCAL_LANG)
4365  {
4366  if (isset($LOCAL_LANG[$this->lang][$index][0]['target'])) {
4367  return $LOCAL_LANG[$this->lang][$index][0]['target'];
4368  } elseif (isset($LOCAL_LANG['default'][$index][0]['target'])) {
4369  return $LOCAL_LANG['default'][$index][0]['target'];
4370  }
4371  return false;
4372  }
4373 
4379  public function initLLvars()
4380  {
4381  // Init languageDependencies list
4382  $this->languageDependencies = [];
4383  // Setting language key and split index:
4384  $this->lang = $this->config['config']['language'] ?: 'default';
4385  $this->pageRenderer->setLanguage($this->lang);
4386 
4387  // Finding the requested language in this list based
4388  // on the $lang key being inputted to this function.
4390  $locales = GeneralUtility::makeInstance(Locales::class);
4391  $locales->initialize();
4392 
4393  // Language is found. Configure it:
4394  if (in_array($this->lang, $locales->getLocales())) {
4395  $this->languageDependencies[] = $this->lang;
4396  foreach ($locales->getLocaleDependencies($this->lang) as $language) {
4397  $this->languageDependencies[] = $language;
4398  }
4399  }
4400 
4401  // Rendering charset of HTML page.
4402  if ($this->config['config']['metaCharset']) {
4404  $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
4405  $this->metaCharset = $charsetConverter->parse_charset($this->config['config']['metaCharset']);
4406  }
4407  }
4408 
4421  public function csConv($str, $from = '')
4422  {
4424  if ($from) {
4426  $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
4427  $output = $charsetConverter->conv($str, $charsetConverter->parse_charset($from), 'utf-8');
4428  return $output ?: $str;
4429  } else {
4430  return $str;
4431  }
4432  }
4433 
4440  public function convOutputCharset($content)
4441  {
4442  if ($this->metaCharset !== 'utf-8') {
4444  $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
4445  $content = $charsetConverter->conv($content, 'utf-8', $this->metaCharset, true);
4446  }
4447  return $content;
4448  }
4449 
4455  public function convPOSTCharset()
4456  {
4457  if ($this->metaCharset !== 'utf-8' && is_array($_POST) && !empty($_POST)) {
4459  $charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class);
4460  $charsetConverter->convArray($_POST, $this->metaCharset, 'utf-8');
4461  $GLOBALS['HTTP_POST_VARS'] = $_POST;
4462  }
4463  }
4464 
4470  protected function calculatePageCacheTimeout()
4471  {
4472  $result = PHP_INT_MAX;
4473  // Get the configuration
4474  $tablesToConsider = $this->getCurrentPageCacheConfiguration();
4475  // Get the time, rounded to the minute (do not polute MySQL cache!)
4476  // It is ok that we do not take seconds into account here because this
4477  // value will be substracted later. So we never get the time "before"
4478  // the cache change.
4479  $now = $GLOBALS['ACCESS_TIME'];
4480  // Find timeout by checking every table
4481  foreach ($tablesToConsider as $tableDef) {
4482  $result = min($result, $this->getFirstTimeValueForRecord($tableDef, $now));
4483  }
4484  // We return + 1 second just to ensure that cache is definitely regenerated
4485  return $result == PHP_INT_MAX ? PHP_INT_MAX : $result - $now + 1;
4486  }
4487 
4503  {
4504  $result = ['tt_content:' . $this->id];
4505  if (isset($this->config['config']['cache.'][$this->id])) {
4506  $result = array_merge($result, GeneralUtility::trimExplode(',', $this->config['config']['cache.'][$this->id]));
4507  }
4508  if (isset($this->config['config']['cache.']['all'])) {
4509  $result = array_merge($result, GeneralUtility::trimExplode(',', $this->config['config']['cache.']['all']));
4510  }
4511  return array_unique($result);
4512  }
4513 
4523  protected function getFirstTimeValueForRecord($tableDef, $now)
4524  {
4525  $now = (int)$now;
4526  $result = PHP_INT_MAX;
4527  list($tableName, $pid) = GeneralUtility::trimExplode(':', $tableDef);
4528  if (empty($tableName) || empty($pid)) {
4529  throw new \InvalidArgumentException('Unexpected value for parameter $tableDef. Expected <tablename>:<pid>, got \'' . htmlspecialchars($tableDef) . '\'.', 1307190365);
4530  }
4531 
4532  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
4533  ->getQueryBuilderForTable($tableName);
4534  $queryBuilder->getRestrictions()
4535  ->removeByType(StartTimeRestriction::class)
4536  ->removeByType(EndTimeRestriction::class);
4537  $timeFields = [];
4538  $timeConditions = $queryBuilder->expr()->orX();
4539  foreach (['starttime', 'endtime'] as $field) {
4540  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field])) {
4541  $timeFields[$field] = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field];
4542  $queryBuilder->addSelectLiteral(
4543  'MIN('
4544  . 'CASE WHEN '
4545  . $queryBuilder->expr()->lte(
4546  $timeFields[$field],
4547  $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT)
4548  )
4549  . ' THEN NULL ELSE ' . $queryBuilder->quoteIdentifier($timeFields[$field]) . ' END'
4550  . ') AS ' . $queryBuilder->quoteIdentifier($timeFields[$field])
4551  );
4552  $timeConditions->add(
4553  $queryBuilder->expr()->gt(
4554  $timeFields[$field],
4555  $queryBuilder->createNamedParameter($now, \PDO::PARAM_INT)
4556  )
4557  );
4558  }
4559  }
4560 
4561  // if starttime or endtime are defined, evaluate them
4562  if (!empty($timeFields)) {
4563  // find the timestamp, when the current page's content changes the next time
4564  $row = $queryBuilder
4565  ->from($tableName)
4566  ->where(
4567  $queryBuilder->expr()->eq(
4568  'pid',
4569  $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)
4570  ),
4571  $timeConditions
4572  )
4573  ->execute()
4574  ->fetch();
4575 
4576  if ($row) {
4577  foreach ($timeFields as $timeField => $_) {
4578  // if a MIN value is found, take it into account for the
4579  // cache lifetime we have to filter out start/endtimes < $now,
4580  // as the SQL query also returns rows with starttime < $now
4581  // and endtime > $now (and using a starttime from the past
4582  // would be wrong)
4583  if ($row[$timeField] !== null && (int)$row[$timeField] > $now) {
4584  $result = min($result, (int)$row[$timeField]);
4585  }
4586  }
4587  }
4588  }
4589 
4590  return $result;
4591  }
4592 
4598  protected function getSysDomainCache()
4599  {
4600  $entryIdentifier = 'core-database-sys_domain-complete';
4602  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4603 
4604  $sysDomainData = [];
4605  if ($runtimeCache->has($entryIdentifier)) {
4606  $sysDomainData = $runtimeCache->get($entryIdentifier);
4607  } else {
4608  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_domain');
4609  $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
4610  $result = $queryBuilder
4611  ->select('uid', 'pid', 'domainName', 'forced')
4612  ->from('sys_domain')
4613  ->where(
4614  $queryBuilder->expr()->eq(
4615  'redirectTo',
4616  $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
4617  )
4618  )
4619  ->orderBy('sorting', 'ASC')
4620  ->execute();
4621 
4622  while ($row = $result->fetch()) {
4623  // if there is already an entry for this pid, check if we should overwrite it
4624  if (isset($sysDomainData[$row['pid']])) {
4625  // There is already a "forced" entry, which must not be overwritten
4626  if ($sysDomainData[$row['pid']]['forced']) {
4627  continue;
4628  }
4629 
4630  // The current domain record is also NOT-forced, keep the old unless the new one matches the current request
4631  if (!$row['forced'] && !$this->domainNameMatchesCurrentRequest($row['domainName'])) {
4632  continue;
4633  }
4634  }
4635 
4636  // as we passed all previous checks, we save this domain for the current pid
4637  $sysDomainData[$row['pid']] = [
4638  'uid' => $row['uid'],
4639  'pid' => $row['pid'],
4640  'domainName' => rtrim($row['domainName'], '/'),
4641  'forced' => $row['forced'],
4642  ];
4643  }
4644  $runtimeCache->set($entryIdentifier, $sysDomainData);
4645  }
4646  return $sysDomainData;
4647  }
4648 
4656  public function domainNameMatchesCurrentRequest($domainName)
4657  {
4658  $currentDomain = GeneralUtility::getIndpEnv('HTTP_HOST');
4659  $currentPathSegment = trim(preg_replace('|/[^/]*$|', '', GeneralUtility::getIndpEnv('SCRIPT_NAME')));
4660  return $currentDomain === $domainName || $currentDomain . $currentPathSegment === $domainName;
4661  }
4662 
4671  public function getDomainDataForPid($targetPid)
4672  {
4673  // Using array_key_exists() here, nice $result can be NULL
4674  // (happens, if there's no domain records defined)
4675  if (!array_key_exists($targetPid, $this->domainDataCache)) {
4676  $result = null;
4677  $sysDomainData = $this->getSysDomainCache();
4678  $rootline = $this->sys_page->getRootLine($targetPid);
4679  // walk the rootline downwards from the target page
4680  // to the root page, until a domain record is found
4681  foreach ($rootline as $pageInRootline) {
4682  $pidInRootline = $pageInRootline['uid'];
4683  if (isset($sysDomainData[$pidInRootline])) {
4684  $result = $sysDomainData[$pidInRootline];
4685  break;
4686  }
4687  }
4688  $this->domainDataCache[$targetPid] = $result;
4689  }
4690 
4691  return $this->domainDataCache[$targetPid];
4692  }
4693 
4701  public function getDomainNameForPid($targetPid)
4702  {
4703  $domainData = $this->getDomainDataForPid($targetPid);
4704  return $domainData ? $domainData['domainName'] : null;
4705  }
4706 
4713  public function getRequestedId()
4714  {
4715  return $this->requestedId ?: $this->id;
4716  }
4717 
4727  protected function acquireLock($type, $key)
4728  {
4729  $lockFactory = GeneralUtility::makeInstance(LockFactory::class);
4730  $this->locks[$type]['accessLock'] = $lockFactory->createLocker($type);
4731 
4732  $this->locks[$type]['pageLock'] = $lockFactory->createLocker(
4733  $key,
4735  );
4736 
4737  do {
4738  if (!$this->locks[$type]['accessLock']->acquire()) {
4739  throw new \RuntimeException('Could not acquire access lock for "' . $type . '"".', 1294586098);
4740  }
4741 
4742  try {
4743  $locked = $this->locks[$type]['pageLock']->acquire(
4745  );
4746  } catch (LockAcquireWouldBlockException $e) {
4747  // somebody else has the lock, we keep waiting
4748 
4749  // first release the access lock
4750  $this->locks[$type]['accessLock']->release();
4751  // now lets make a short break (100ms) until we try again, since
4752  // the page generation by the lock owner will take a while anyways
4753  usleep(100000);
4754  continue;
4755  }
4756  $this->locks[$type]['accessLock']->release();
4757  if ($locked) {
4758  break;
4759  } else {
4760  throw new \RuntimeException('Could not acquire page lock for ' . $key . '.', 1460975877);
4761  }
4762  } while (true);
4763  }
4764 
4773  protected function releaseLock($type)
4774  {
4775  if ($this->locks[$type]['accessLock']) {
4776  if (!$this->locks[$type]['accessLock']->acquire()) {
4777  throw new \RuntimeException('Could not acquire access lock for "' . $type . '"".', 1460975902);
4778  }
4779 
4780  $this->locks[$type]['pageLock']->release();
4781  $this->locks[$type]['pageLock']->destroy();
4782  $this->locks[$type]['pageLock'] = null;
4783 
4784  $this->locks[$type]['accessLock']->release();
4785  $this->locks[$type]['accessLock'] = null;
4786  }
4787  }
4788 
4794  protected function getBackendUser()
4795  {
4796  return $GLOBALS['BE_USER'];
4797  }
4798 
4802  protected function getTimeTracker()
4803  {
4804  return GeneralUtility::makeInstance(TimeTracker::class);
4805  }
4806 
4812  protected function getDocumentTemplate()
4813  {
4814  return $GLOBALS['TBE_TEMPLATE'];
4815  }
4816 }
__construct($_=null, $id, $type, $no_cache= '', $cHash= '', $_2=null, $MP= '', $RDCT= '')
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static hideIfNotTranslated($l18n_cfg_fieldValue)
static isFirstPartOfStr($str, $partStr)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static hideIfDefaultLanguage($localizationConfiguration)
static implodeArrayForUrl($name, array $theArray, $str= '', $skipBlank=false, $rawurlencodeParamName=false)
getPageShortcut($SC, $mode, $thisUid, $itera=20, $pageLog=[], $disableGroupCheck=false)
static setSingletonInstance($className, SingletonInterface $instance)
static _GETset($inputGet, $key= '')
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static stripLogicalOperatorPrefix(string $constraint)
static getFileAbsFileName($filename, $_=null, $_2=null)
static isAllowedLinkVarValue($haystack, $needle)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)
static devLog($msg, $extKey, $severity=0, $dataVar=false)