TYPO3 CMS  TYPO3_7-6
TypoScriptFrontendController.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
48 
65 {
70  public $id = '';
71 
76  public $type = '';
77 
82  public $cHash = '';
83 
89  public $no_cache = false;
90 
95  public $rootLine = '';
96 
101  public $page = '';
102 
108  public $contentPid = 0;
109 
117  protected $originalMountPointPage = null;
118 
127  protected $originalShortcutPage = null;
128 
134  public $sys_page = '';
135 
140  public $jumpurl = '';
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 $TYPO3_CONF_VARS = [];
285 
291  public $config = [];
292 
298  public $tmpl = null;
299 
305  public $cacheTimeOutDefault = false;
306 
312  public $cacheContentFlag = false;
313 
318  public $cacheExpires = 0;
319 
324  public $isClientCachable = false;
325 
332  public $all = [];
333 
338  public $sPre = '';
339 
345  public $pSetup = '';
346 
352  public $newHash = '';
353 
361  public $getMethodUrlIdToken = '';
362 
370  public $no_cacheBeforePageGen = false;
371 
377  public $tempContent = false;
378 
383  public $forceTemplateParsing = false;
384 
389  public $cHash_array = [];
390 
395  public $pagesTSconfig = '';
396 
415 
421 
429 
435  public $additionalCSS = [];
436 
445  public $JSeventFuncCalls = [
446  'onmousemove' => [],
447  'onmouseup' => [],
448  'onkeydown' => [],
449  'onkeyup' => [],
450  'onkeypress' => [],
451  'onload' => [],
452  'onunload' => []
453  ];
454 
458  public $JSCode;
459 
463  public $inlineJS;
464 
469  public $divSection = '';
470 
476  public $defaultBodyTag = '<body>';
477 
482  public $debug = '';
483 
488  public $intTarget = '';
489 
494  public $extTarget = '';
495 
500  public $fileTarget = '';
501 
507  public $MP_defaults = [];
508 
514 
519  public $absRefPrefix = '';
520 
526 
531  public $lockFilePath = '';
532 
537  public $ATagParams = '';
538 
545  public $sWordRegEx = '';
546 
552  public $sWordList = '';
553 
560  public $linkVars = '';
561 
568  public $excludeCHashVars = '';
569 
575  public $displayEditIcons = '';
576 
584 
592  public $sys_language_uid = 0;
593 
598  public $sys_language_mode = '';
599 
606 
614 
621 
627  public $applicationData = [];
628 
632  public $register = [];
633 
639  public $registerStack = [];
640 
646  public $cObjectDepthCounter = 50;
647 
653  public $recordRegister = [];
654 
662  public $currentRecord = '';
663 
669  public $accessKey = [];
670 
676  public $imagesOnPage = [];
677 
683  public $lastImageInfo = [];
684 
690  public $uniqueCounter = 0;
691 
695  public $uniqueString = '';
696 
702  public $indexedDocTitle = '';
703 
709  public $altPageTitle = '';
710 
715  public $baseUrl = '';
716 
723  public $anchorPrefix = '';
724 
729  private $usedUniqueIds = [];
730 
736  public $cObj = '';
737 
742  public $content = '';
743 
748  public $clientInfo = '';
749 
753  public $scriptParseTime = 0;
754 
761  public $csConvObj;
762 
767  public $defaultCharSet = 'utf-8';
768 
773  public $renderCharset = 'utf-8';
774 
781  public $metaCharset = 'utf-8';
782 
787  public $localeCharset = '';
788 
793  public $lang = '';
794 
798  public $LL_labels_cache = [];
799 
803  public $LL_files_cache = [];
804 
812  protected $languageDependencies = [];
813 
817  protected $locks = [];
818 
822  protected $pageRenderer = null;
823 
830  protected $pageCache;
831 
835  protected $pageCacheTags = [];
836 
842  protected $cacheHash;
843 
849  protected $domainDataCache = [];
850 
858  protected $contentType = 'text/html';
859 
867  public $xhtmlDoctype = '';
868 
873 
879  protected $requestedId;
880 
885 
902  public function __construct($TYPO3_CONF_VARS, $id, $type, $no_cache = '', $cHash = '', $jumpurl = '', $MP = '', $RDCT = '')
903  {
904  // Setting some variables:
905  $this->TYPO3_CONF_VARS = $TYPO3_CONF_VARS;
906  $this->id = $id;
907  $this->type = $type;
908  if ($no_cache) {
909  if ($this->TYPO3_CONF_VARS['FE']['disableNoCacheParameter']) {
910  $warning = '&no_cache=1 has been ignored because $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set!';
911  $this->getTimeTracker()->setTSlogMessage($warning, 2);
912  } else {
913  $warning = '&no_cache=1 has been supplied, so caching is disabled! URL: "' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL') . '"';
914  $this->disableCache();
915  }
916  GeneralUtility::sysLog($warning, 'cms', GeneralUtility::SYSLOG_SEVERITY_WARNING);
917  }
918  $this->cHash = $cHash;
919  $this->jumpurl = $jumpurl;
920  $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? (string)$MP : '';
921  $this->RDCT = $RDCT;
922  $this->clientInfo = GeneralUtility::clientInfo();
923  $this->uniqueString = md5(microtime());
924  $this->csConvObj = GeneralUtility::makeInstance(CharsetConverter::class);
925  $this->initPageRenderer();
926  // Call post processing function for constructor:
927  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'])) {
928  $_params = ['pObj' => &$this];
929  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-PostProc'] as $_funcRef) {
930  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
931  }
932  }
933  $this->cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
934  $this->initCaches();
935  }
936 
940  protected function initPageRenderer()
941  {
942  if ($this->pageRenderer !== null) {
943  return;
944  }
945  $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
946  $this->pageRenderer->setTemplateFile('EXT:frontend/Resources/Private/Templates/MainPage.html');
947  $this->pageRenderer->setBackPath(TYPO3_mainDir);
948  }
949 
954  public function setContentType($contentType)
955  {
956  $this->contentType = $contentType;
957  }
958 
967  public function connectToDB()
968  {
969  try {
970  $this->getDatabaseConnection()->connectDB();
971  } catch (\RuntimeException $exception) {
972  switch ($exception->getCode()) {
973  case 1270853883:
974  // Cannot connect to current database
975  $message = 'Cannot connect to the configured database "' . TYPO3_db . '"';
976  if ($this->checkPageUnavailableHandler()) {
977  $this->pageUnavailableAndExit($message);
978  } else {
979  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
980  throw new ServiceUnavailableException($message, 1301648782);
981  }
982  break;
983  case 1270853884:
984  // Username / password not accepted
985  $message = 'The current username, password or host was not accepted when' . ' the connection to the database was attempted to be established!';
986  if ($this->checkPageUnavailableHandler()) {
987  $this->pageUnavailableAndExit($message);
988  } else {
989  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
990  throw new ServiceUnavailableException('Database Error: ' . $message, 1301648945);
991  }
992  break;
993  default:
994  throw $exception;
995  }
996  }
997  // Call post processing function for DB connection:
998  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'])) {
999  $_params = ['pObj' => &$this];
1000  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['connectToDB'] as $_funcRef) {
1001  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1002  }
1003  }
1004  }
1005 
1014  public function sendRedirect()
1015  {
1016  if ($this->RDCT) {
1017  $db = $this->getDatabaseConnection();
1018  $row = $db->exec_SELECTgetSingleRow('params', 'cache_md5params', 'md5hash=' . $db->fullQuoteStr($this->RDCT, 'cache_md5params'));
1019  if ($row) {
1020  $this->updateMD5paramsRecord($this->RDCT);
1021  header('Location: ' . $row['params']);
1022  die;
1023  }
1024  }
1025  }
1026 
1033  public function getPageRenderer()
1034  {
1036  $this->initPageRenderer();
1037 
1038  return $this->pageRenderer;
1039  }
1040 
1041  /********************************************
1042  *
1043  * Initializing, resolving page id
1044  *
1045  ********************************************/
1051  protected function initCaches()
1052  {
1053  $this->pageCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pages');
1054  }
1055 
1061  public function initFEuser()
1062  {
1063  $this->fe_user = GeneralUtility::makeInstance(FrontendUserAuthentication::class);
1064  $this->fe_user->lockIP = $this->TYPO3_CONF_VARS['FE']['lockIP'];
1065  $this->fe_user->checkPid = $this->TYPO3_CONF_VARS['FE']['checkFeUserPid'];
1066  $this->fe_user->lifetime = (int)$this->TYPO3_CONF_VARS['FE']['lifetime'];
1067  // List of pid's acceptable
1068  $pid = GeneralUtility::_GP('pid');
1069  $this->fe_user->checkPid_value = $pid ? $this->getDatabaseConnection()->cleanIntList($pid) : 0;
1070  // Check if a session is transferred:
1071  if (GeneralUtility::_GP('FE_SESSION_KEY')) {
1072  $fe_sParts = explode('-', GeneralUtility::_GP('FE_SESSION_KEY'));
1073  // If the session key hash check is OK:
1074  if (md5(($fe_sParts[0] . '/' . $this->TYPO3_CONF_VARS['SYS']['encryptionKey'])) === (string)$fe_sParts[1]) {
1076  $_COOKIE[$cookieName] = $fe_sParts[0];
1077  if (isset($_SERVER['HTTP_COOKIE'])) {
1078  // See http://forge.typo3.org/issues/27740
1079  $_SERVER['HTTP_COOKIE'] .= ';' . $cookieName . '=' . $fe_sParts[0];
1080  }
1081  $this->fe_user->forceSetCookie = 1;
1082  $this->fe_user->dontSetCookie = false;
1083  unset($cookieName);
1084  }
1085  }
1086  $this->fe_user->start();
1087  $this->fe_user->unpack_uc('');
1088  // Gets session data
1089  $this->fe_user->fetchSessionData();
1090  if (!empty($GLOBALS['TYPO3_CONF_VARS']['FE']['enableRecordRegistration'])) {
1091  $recs = GeneralUtility::_GP('recs');
1092  // If any record registration is submitted, register the record.
1093  if (is_array($recs)) {
1094  $this->fe_user->record_registration($recs, $this->TYPO3_CONF_VARS['FE']['maxSessionDataSize']);
1095  }
1096  }
1097  // Call hook for possible manipulation of frontend user object
1098  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'])) {
1099  $_params = ['pObj' => &$this];
1100  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['initFEuser'] as $_funcRef) {
1101  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1102  }
1103  }
1104  // For every 60 seconds the is_online timestamp is updated.
1105  if (is_array($this->fe_user->user) && $this->fe_user->user['uid'] && $this->fe_user->user['is_online'] < $GLOBALS['EXEC_TIME'] - 60) {
1106  $this->getDatabaseConnection()->exec_UPDATEquery('fe_users', 'uid=' . (int)$this->fe_user->user['uid'], ['is_online' => $GLOBALS['EXEC_TIME']]);
1107  }
1108  }
1109 
1116  public function initUserGroups()
1117  {
1118  // This affects the hidden-flag selecting the fe_groups for the user!
1119  $this->fe_user->showHiddenRecords = $this->showHiddenRecords;
1120  // no matter if we have an active user we try to fetch matching groups which can be set without an user (simulation for instance!)
1121  $this->fe_user->fetchGroupData();
1122  if (is_array($this->fe_user->user) && !empty($this->fe_user->groupData['uid'])) {
1123  // global flag!
1124  $this->loginUser = true;
1125  // 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!
1126  $this->gr_list = '0,-2';
1127  $gr_array = $this->fe_user->groupData['uid'];
1128  } else {
1129  $this->loginUser = false;
1130  // 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!
1131  $this->gr_list = '0,-1';
1132  if ($this->loginAllowedInBranch) {
1133  // 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.
1134  $gr_array = $this->fe_user->groupData['uid'];
1135  } else {
1136  // Set to blank since we will NOT risk any groups being set when no logins are allowed!
1137  $gr_array = [];
1138  }
1139  }
1140  // Clean up.
1141  // Make unique...
1142  $gr_array = array_unique($gr_array);
1143  // sort
1144  sort($gr_array);
1145  if (!empty($gr_array) && !$this->loginAllowedInBranch_mode) {
1146  $this->gr_list .= ',' . implode(',', $gr_array);
1147  }
1148  if ($this->fe_user->writeDevLog) {
1149  GeneralUtility::devLog('Valid usergroups for TSFE: ' . $this->gr_list, __CLASS__);
1150  }
1151  }
1152 
1158  public function isUserOrGroupSet()
1159  {
1160  return is_array($this->fe_user->user) || $this->gr_list !== '0,-1';
1161  }
1162 
1172  public function checkAlternativeIdMethods()
1173  {
1174  $this->siteScript = GeneralUtility::getIndpEnv('TYPO3_SITE_SCRIPT');
1175  // Call post processing function for custom URL methods.
1176  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'])) {
1177  $_params = ['pObj' => &$this];
1178  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkAlternativeIdMethods-PostProc'] as $_funcRef) {
1179  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1180  }
1181  }
1182  }
1183 
1191  public function clear_preview()
1192  {
1193  $this->showHiddenPage = false;
1194  $this->showHiddenRecords = false;
1195  $GLOBALS['SIM_EXEC_TIME'] = $GLOBALS['EXEC_TIME'];
1196  $GLOBALS['SIM_ACCESS_TIME'] = $GLOBALS['ACCESS_TIME'];
1197  $this->fePreview = 0;
1198  }
1199 
1205  public function isBackendUserLoggedIn()
1206  {
1207  return (bool)$this->beUserLogin;
1208  }
1209 
1215  public function initializeBackendUser()
1216  {
1217  // PRE BE_USER HOOK
1218  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'])) {
1219  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['preBeUser'] as $_funcRef) {
1220  $_params = [];
1221  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1222  }
1223  }
1225  $BE_USER = null;
1226  // If the backend cookie is set,
1227  // we proceed and check if a backend user is logged in.
1228  if ($_COOKIE[BackendUserAuthentication::getCookieName()]) {
1229  $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] = microtime(true);
1230  $this->getTimeTracker()->push('Back End user initialized', '');
1231  // @todo validate the comment below: is this necessary? if so,
1232  // formfield_status should be set to "" in \TYPO3\CMS\Backend\FrontendBackendUserAuthentication
1233  // which is a subclass of \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
1234  // ----
1235  // the value this->formfield_status is set to empty in order to
1236  // disable login-attempts to the backend account through this script
1237  // New backend user object
1238  $BE_USER = GeneralUtility::makeInstance(FrontendBackendUserAuthentication::class);
1239  $BE_USER->lockIP = $this->TYPO3_CONF_VARS['BE']['lockIP'];
1240  // Object is initialized
1241  $BE_USER->start();
1242  $BE_USER->unpack_uc('');
1243  if (!empty($BE_USER->user['uid'])) {
1244  $BE_USER->fetchGroupData();
1245  $this->beUserLogin = true;
1246  }
1247  // Unset the user initialization.
1248  if (!$BE_USER->checkLockToIP() || !$BE_USER->checkBackendAccessSettingsFromInitPhp() || empty($BE_USER->user['uid'])) {
1249  $BE_USER = null;
1250  $this->beUserLogin = false;
1251  }
1252  $this->getTimeTracker()->pull();
1253  $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] = microtime(true);
1254  }
1255  // POST BE_USER HOOK
1256  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'])) {
1257  $_params = [
1258  'BE_USER' => &$BE_USER
1259  ];
1260  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/index_ts.php']['postBeUser'] as $_funcRef) {
1261  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1262  }
1263  }
1264  return $BE_USER;
1265  }
1266 
1275  public function determineId()
1276  {
1277  // Call pre processing function for id determination
1278  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'])) {
1279  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PreProcessing'] as $functionReference) {
1280  $parameters = ['parentObject' => $this];
1281  GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1282  }
1283  }
1284  // Getting ARG-v values if some
1285  $this->setIDfromArgV();
1286  // If there is a Backend login we are going to check for any preview settings:
1287  $this->getTimeTracker()->push('beUserLogin', '');
1288  $originalFrontendUser = null;
1289  $backendUser = $this->getBackendUser();
1290  if ($this->beUserLogin || $this->doWorkspacePreview()) {
1291  // Backend user preview features:
1292  if ($this->beUserLogin && $backendUser->adminPanel instanceof AdminPanelView) {
1293  $this->fePreview = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview');
1294  // If admin panel preview is enabled...
1295  if ($this->fePreview) {
1296  if ($this->fe_user->user) {
1297  $originalFrontendUser = $this->fe_user->user;
1298  }
1299  $this->showHiddenPage = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenPages');
1300  $this->showHiddenRecords = (bool)$backendUser->adminPanel->extGetFeAdminValue('preview', 'showHiddenRecords');
1301  // Simulate date
1302  $simTime = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateDate');
1303  if ($simTime) {
1304  $GLOBALS['SIM_EXEC_TIME'] = $simTime;
1305  $GLOBALS['SIM_ACCESS_TIME'] = $simTime - $simTime % 60;
1306  }
1307  // simulate user
1308  $simUserGroup = $backendUser->adminPanel->extGetFeAdminValue('preview', 'simulateUserGroup');
1309  $this->simUserGroup = $simUserGroup;
1310  if ($simUserGroup) {
1311  if ($this->fe_user->user) {
1312  $this->fe_user->user[$this->fe_user->usergroup_column] = $simUserGroup;
1313  } else {
1314  $this->fe_user->user = [
1315  $this->fe_user->usergroup_column => $simUserGroup
1316  ];
1317  }
1318  }
1319  if (!$simUserGroup && !$simTime && !$this->showHiddenPage && !$this->showHiddenRecords) {
1320  $this->fePreview = 0;
1321  }
1322  }
1323  }
1324  if ($this->id) {
1325  if ($this->determineIdIsHiddenPage()) {
1326  // The preview flag is set only if the current page turns out to actually be hidden!
1327  $this->fePreview = 1;
1328  $this->showHiddenPage = true;
1329  }
1330  // For Live workspace: Check root line for proper connection to tree root (done because of possible preview of page / branch versions)
1331  if (!$this->fePreview && $this->whichWorkspace() === 0) {
1332  // Initialize the page-select functions to check rootline:
1333  $temp_sys_page = GeneralUtility::makeInstance(PageRepository::class);
1334  $temp_sys_page->init($this->showHiddenPage);
1335  // If root line contained NO records and ->error_getRootLine_failPid tells us that it was because of a pid=-1 (indicating a "version" record)...:
1336  if (empty($temp_sys_page->getRootLine($this->id, $this->MP)) && $temp_sys_page->error_getRootLine_failPid == -1) {
1337  // Setting versioningPreview flag and try again:
1338  $temp_sys_page->versioningPreview = true;
1339  if (!empty($temp_sys_page->getRootLine($this->id, $this->MP))) {
1340  // 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.
1341  $this->fePreview = 1;
1342  }
1343  }
1344  }
1345  }
1346  // The preview flag will be set if a backend user is in an offline workspace
1347  if (
1348  (
1349  $backendUser->user['workspace_preview']
1350  || GeneralUtility::_GP('ADMCMD_view')
1351  || $this->doWorkspacePreview()
1352  )
1353  && (
1354  $this->whichWorkspace() === -1
1355  || $this->whichWorkspace() > 0
1356  )
1357  && !GeneralUtility::_GP('ADMCMD_noBeUser')
1358  ) {
1359  // Will show special preview message.
1360  $this->fePreview = 2;
1361  }
1362  // If the front-end is showing a preview, caching MUST be disabled.
1363  if ($this->fePreview) {
1364  $this->disableCache();
1365  }
1366  }
1367  $this->getTimeTracker()->pull();
1368  // Now, get the id, validate access etc:
1369  $this->fetch_the_id();
1370  // Check if backend user has read access to this page. If not, recalculate the id.
1371  if ($this->beUserLogin && $this->fePreview) {
1372  if (!$backendUser->doesUserHaveAccess($this->page, 1)) {
1373  // Resetting
1374  $this->clear_preview();
1375  $this->fe_user->user = $originalFrontendUser;
1376  // Fetching the id again, now with the preview settings reset.
1377  $this->fetch_the_id();
1378  }
1379  }
1380  // Checks if user logins are blocked for a certain branch and if so, will unset user login and re-fetch ID.
1381  $this->loginAllowedInBranch = $this->checkIfLoginAllowedInBranch();
1382  // Logins are not allowed:
1383  if (!$this->loginAllowedInBranch) {
1384  // Only if there is a login will we run this...
1385  if ($this->isUserOrGroupSet()) {
1386  if ($this->loginAllowedInBranch_mode == 'all') {
1387  // Clear out user and group:
1388  $this->fe_user->hideActiveLogin();
1389  $this->gr_list = '0,-1';
1390  } else {
1391  $this->gr_list = '0,-2';
1392  }
1393  // Fetching the id again, now with the preview settings reset.
1394  $this->fetch_the_id();
1395  }
1396  }
1397  // Final cleaning.
1398  // Make sure it's an integer
1399  $this->id = ($this->contentPid = (int)$this->id);
1400  // Make sure it's an integer
1401  $this->type = (int)$this->type;
1402  // Call post processing function for id determination:
1403  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'])) {
1404  $_params = ['pObj' => &$this];
1405  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'] as $_funcRef) {
1406  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1407  }
1408  }
1409  }
1410 
1417  protected function determineIdIsHiddenPage()
1418  {
1419  $field = MathUtility::canBeInterpretedAsInteger($this->id) ? 'uid' : 'alias';
1420  $pageSelectCondition = $field . '=' . $this->getDatabaseConnection()->fullQuoteStr($this->id, 'pages');
1421  $page = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid,hidden,starttime,endtime', 'pages', $pageSelectCondition . ' AND pid>=0 AND deleted=0');
1422  $workspace = $this->whichWorkspace();
1423  if ($workspace !== 0 && $workspace !== false) {
1424  // Fetch overlay of page if in workspace and check if it is hidden
1425  $pageSelectObject = GeneralUtility::makeInstance(PageRepository::class);
1426  $pageSelectObject->versioningPreview = true;
1427  $pageSelectObject->init(false);
1428  $targetPage = $pageSelectObject->getWorkspaceVersionOfRecord($this->whichWorkspace(), 'pages', $page['uid']);
1429  $result = $targetPage === -1 || $targetPage === -2;
1430  } else {
1431  $result = is_array($page) && ($page['hidden'] || $page['starttime'] > $GLOBALS['SIM_EXEC_TIME'] || $page['endtime'] != 0 && $page['endtime'] <= $GLOBALS['SIM_EXEC_TIME']);
1432  }
1433  return $result;
1434  }
1435 
1445  public function fetch_the_id()
1446  {
1447  $timeTracker = $this->getTimeTracker();
1448  $timeTracker->push('fetch_the_id initialize/', '');
1449  // Initialize the page-select functions.
1450  $this->sys_page = GeneralUtility::makeInstance(PageRepository::class);
1451  $this->sys_page->versioningPreview = $this->fePreview === 2 || (int)$this->workspacePreview || (bool)GeneralUtility::_GP('ADMCMD_view');
1452  $this->sys_page->versioningWorkspaceId = $this->whichWorkspace();
1453  $this->sys_page->init($this->showHiddenPage);
1454  // Set the valid usergroups for FE
1455  $this->initUserGroups();
1456  // Sets sys_page where-clause
1457  $this->setSysPageWhereClause();
1458  // Splitting $this->id by a period (.).
1459  // First part is 'id' and second part (if exists) will overrule the &type param
1460  $idParts = explode('.', $this->id, 2);
1461  $this->id = $idParts[0];
1462  if (isset($idParts[1])) {
1463  $this->type = $idParts[1];
1464  }
1465 
1466  // If $this->id is a string, it's an alias
1467  $this->checkAndSetAlias();
1468  // The id and type is set to the integer-value - just to be sure...
1469  $this->id = (int)$this->id;
1470  $this->type = (int)$this->type;
1471  $timeTracker->pull();
1472  // We find the first page belonging to the current domain
1473  $timeTracker->push('fetch_the_id domain/', '');
1474  // The page_id of the current domain
1475  $this->domainStartPage = $this->findDomainRecord($this->TYPO3_CONF_VARS['SYS']['recursiveDomainSearch']);
1476  if (!$this->id) {
1477  if ($this->domainStartPage) {
1478  // If the id was not previously set, set it to the id of the domain.
1479  $this->id = $this->domainStartPage;
1480  } else {
1481  // Find the first 'visible' page in that domain
1482  $theFirstPage = $this->sys_page->getFirstWebPage($this->id);
1483  if ($theFirstPage) {
1484  $this->id = $theFirstPage['uid'];
1485  } else {
1486  $message = 'No pages are found on the rootlevel!';
1487  if ($this->checkPageUnavailableHandler()) {
1488  $this->pageUnavailableAndExit($message);
1489  } else {
1490  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1491  throw new ServiceUnavailableException($message, 1301648975);
1492  }
1493  }
1494  }
1495  }
1496  $timeTracker->pull();
1497  $timeTracker->push('fetch_the_id rootLine/', '');
1498  // We store the originally requested id
1499  $this->requestedId = $this->id;
1500  $this->getPageAndRootlineWithDomain($this->domainStartPage);
1501  $timeTracker->pull();
1502  if ($this->pageNotFound && $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1503  $pNotFoundMsg = [
1504  1 => 'ID was not an accessible page',
1505  2 => 'Subsection was found and not accessible',
1506  3 => 'ID was outside the domain',
1507  4 => 'The requested page alias does not exist'
1508  ];
1509  $this->pageNotFoundAndExit($pNotFoundMsg[$this->pageNotFound]);
1510  }
1511  if ($this->page['url_scheme'] > 0) {
1512  $newUrl = '';
1513  $requestUrlScheme = parse_url(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
1514  if ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTP && $requestUrlScheme == 'https') {
1515  $newUrl = 'http://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 8);
1516  } elseif ((int)$this->page['url_scheme'] === HttpUtility::SCHEME_HTTPS && $requestUrlScheme == 'http') {
1517  $newUrl = 'https://' . substr(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'), 7);
1518  }
1519  if ($newUrl !== '') {
1520  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
1521  $headerCode = HttpUtility::HTTP_STATUS_303;
1522  } else {
1523  $headerCode = HttpUtility::HTTP_STATUS_301;
1524  }
1525  HttpUtility::redirect($newUrl, $headerCode);
1526  }
1527  }
1528  // Set no_cache if set
1529  if ($this->page['no_cache']) {
1530  $this->set_no_cache('no_cache is set in page properties');
1531  }
1532  // Init SYS_LASTCHANGED
1533  $this->register['SYS_LASTCHANGED'] = (int)$this->page['tstamp'];
1534  if ($this->register['SYS_LASTCHANGED'] < (int)$this->page['SYS_LASTCHANGED']) {
1535  $this->register['SYS_LASTCHANGED'] = (int)$this->page['SYS_LASTCHANGED'];
1536  }
1537  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'])) {
1538  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['fetchPageId-PostProcessing'] as $functionReference) {
1539  $parameters = ['parentObject' => $this];
1540  GeneralUtility::callUserFunction($functionReference, $parameters, $this);
1541  }
1542  }
1543  }
1544 
1560  public function getPageAndRootline()
1561  {
1562  $this->page = $this->sys_page->getPage($this->id);
1563  if (empty($this->page)) {
1564  // If no page, we try to find the page before in the rootLine.
1565  // Page is 'not found' in case the id itself was not an accessible page. code 1
1566  $this->pageNotFound = 1;
1567  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1568  if (!empty($this->rootLine)) {
1569  $c = count($this->rootLine) - 1;
1570  while ($c > 0) {
1571  // Add to page access failure history:
1572  $this->pageAccessFailureHistory['direct_access'][] = $this->rootLine[$c];
1573  // Decrease to next page in rootline and check the access to that, if OK, set as page record and ID value.
1574  $c--;
1575  $this->id = $this->rootLine[$c]['uid'];
1576  $this->page = $this->sys_page->getPage($this->id);
1577  if (!empty($this->page)) {
1578  break;
1579  }
1580  }
1581  }
1582  // If still no page...
1583  if (empty($this->page)) {
1584  $message = 'The requested page does not exist!';
1585  if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1586  $this->pageNotFoundAndExit($message);
1587  } else {
1588  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1589  throw new PageNotFoundException($message, 1301648780);
1590  }
1591  }
1592  }
1593  // Spacer is not accessible in frontend
1594  if ($this->page['doktype'] == PageRepository::DOKTYPE_SPACER) {
1595  $message = 'The requested page does not exist!';
1596  if ($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling']) {
1597  $this->pageNotFoundAndExit($message);
1598  } else {
1599  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1600  throw new PageNotFoundException($message, 1301648781);
1601  }
1602  }
1603  // Is the ID a link to another page??
1604  if ($this->page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1605  // 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.
1606  $this->MP = '';
1607  // saving the page so that we can check later - when we know
1608  // about languages - whether we took the correct shortcut or
1609  // whether a translation of the page overwrites the shortcut
1610  // target and we need to follow the new target
1611  $this->originalShortcutPage = $this->page;
1612  $this->page = $this->getPageShortcut($this->page['shortcut'], $this->page['shortcut_mode'], $this->page['uid']);
1613  $this->id = $this->page['uid'];
1614  }
1615  // If the page is a mountpoint which should be overlaid with the contents of the mounted page,
1616  // it must never be accessible directly, but only in the mountpoint context. Therefore we change
1617  // the current ID and the user is redirected by checkPageForMountpointRedirect().
1618  if ($this->page['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT && $this->page['mount_pid_ol']) {
1619  $this->originalMountPointPage = $this->page;
1620  $this->page = $this->sys_page->getPage($this->page['mount_pid']);
1621  if (empty($this->page)) {
1622  $message = 'This page (ID ' . $this->originalMountPointPage['uid'] . ') is of type "Mount point" and '
1623  . 'mounts a page which is not accessible (ID ' . $this->originalMountPointPage['mount_pid'] . ').';
1624  throw new PageNotFoundException($message, 1402043263);
1625  }
1626  $this->MP = $this->page['uid'] . '-' . $this->originalMountPointPage['uid'];
1627  $this->id = $this->page['uid'];
1628  }
1629  // Gets the rootLine
1630  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1631  // If not rootline we're off...
1632  if (empty($this->rootLine)) {
1633  $ws = $this->whichWorkspace();
1634  if ($this->sys_page->error_getRootLine_failPid == -1 && $ws) {
1635  $this->sys_page->versioningPreview = true;
1636  $this->sys_page->versioningWorkspaceId = $ws;
1637  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1638  }
1639  if (empty($this->rootLine)) {
1640  $message = 'The requested page didn\'t have a proper connection to the tree-root!';
1641  if ($this->checkPageUnavailableHandler()) {
1642  $this->pageUnavailableAndExit($message);
1643  } else {
1644  $rootline = '(' . $this->sys_page->error_getRootLine . ')';
1645  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1646  throw new ServiceUnavailableException($message . '<br /><br />' . $rootline, 1301648167);
1647  }
1648  }
1649  $this->fePreview = 1;
1650  }
1651  // Checking for include section regarding the hidden/starttime/endtime/fe_user (that is access control of a whole subbranch!)
1652  if ($this->checkRootlineForIncludeSection()) {
1653  if (empty($this->rootLine)) {
1654  $message = 'The requested page was not accessible!';
1655  if ($this->checkPageUnavailableHandler()) {
1656  $this->pageUnavailableAndExit($message);
1657  } else {
1658  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1659  throw new ServiceUnavailableException($message, 1301648234);
1660  }
1661  } else {
1662  $el = reset($this->rootLine);
1663  $this->id = $el['uid'];
1664  $this->page = $this->sys_page->getPage($this->id);
1665  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
1666  }
1667  }
1668  }
1669 
1685  public function getPageShortcut($SC, $mode, $thisUid, $itera = 20, $pageLog = [], $disableGroupCheck = false)
1686  {
1687  $idArray = GeneralUtility::intExplode(',', $SC);
1688  // Find $page record depending on shortcut mode:
1689  switch ($mode) {
1691 
1693  $pageArray = $this->sys_page->getMenu($idArray[0] ? $idArray[0] : $thisUid, '*', 'sorting', 'AND pages.doktype<199 AND pages.doktype!=' . PageRepository::DOKTYPE_BE_USER_SECTION);
1694  $pO = 0;
1695  if ($mode == PageRepository::SHORTCUT_MODE_RANDOM_SUBPAGE && !empty($pageArray)) {
1696  $randval = (int)rand(0, count($pageArray) - 1);
1697  $pO = $randval;
1698  }
1699  $c = 0;
1700  $page = [];
1701  foreach ($pageArray as $pV) {
1702  if ($c == $pO) {
1703  $page = $pV;
1704  break;
1705  }
1706  $c++;
1707  }
1708  if (empty($page)) {
1709  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a subpage. ' . 'However, this page has no accessible subpages.';
1710  throw new PageNotFoundException($message, 1301648328);
1711  }
1712  break;
1714  $parent = $this->sys_page->getPage($idArray[0] ? $idArray[0] : $thisUid, $disableGroupCheck);
1715  $page = $this->sys_page->getPage($parent['pid'], $disableGroupCheck);
1716  if (empty($page)) {
1717  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to its parent page. ' . 'However, the parent page is not accessible.';
1718  throw new PageNotFoundException($message, 1301648358);
1719  }
1720  break;
1721  default:
1722  $page = $this->sys_page->getPage($idArray[0], $disableGroupCheck);
1723  if (empty($page)) {
1724  $message = 'This page (ID ' . $thisUid . ') is of type "Shortcut" and configured to redirect to a page, which is not accessible (ID ' . $idArray[0] . ').';
1725  throw new PageNotFoundException($message, 1301648404);
1726  }
1727  }
1728  // Check if short cut page was a shortcut itself, if so look up recursively:
1729  if ($page['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
1730  if (!in_array($page['uid'], $pageLog) && $itera > 0) {
1731  $pageLog[] = $page['uid'];
1732  $page = $this->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $page['uid'], $itera - 1, $pageLog, $disableGroupCheck);
1733  } else {
1734  $pageLog[] = $page['uid'];
1735  $message = 'Page shortcuts were looping in uids ' . implode(',', $pageLog) . '...!';
1736  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
1737  throw new \RuntimeException($message, 1294587212);
1738  }
1739  }
1740  // Return resulting page:
1741  return $page;
1742  }
1743 
1751  {
1752  $c = count($this->rootLine);
1753  $removeTheRestFlag = 0;
1754  for ($a = 0; $a < $c; $a++) {
1755  if (!$this->checkPagerecordForIncludeSection($this->rootLine[$a])) {
1756  // Add to page access failure history:
1757  $this->pageAccessFailureHistory['sub_section'][] = $this->rootLine[$a];
1758  $removeTheRestFlag = 1;
1759  }
1760  if ($this->rootLine[$a]['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION) {
1761  // If there is a backend user logged in, check if he has read access to the page:
1762  if ($this->beUserLogin) {
1763  $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('uid', 'pages', 'uid=' . (int)$this->id . ' AND ' . $this->getBackendUser()->getPagePermsClause(1));
1764  // versionOL()?
1765  if (!$row) {
1766  // 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...
1767  $removeTheRestFlag = 1;
1768  }
1769  } else {
1770  // Dont go here, if there is no backend user logged in.
1771  $removeTheRestFlag = 1;
1772  }
1773  }
1774  if ($removeTheRestFlag) {
1775  // Page is 'not found' in case a subsection was found and not accessible, code 2
1776  $this->pageNotFound = 2;
1777  unset($this->rootLine[$a]);
1778  }
1779  }
1780  return $removeTheRestFlag;
1781  }
1782 
1793  public function checkEnableFields($row, $bypassGroupCheck = false)
1794  {
1795  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'])) {
1796  $_params = ['pObj' => $this, 'row' => &$row, 'bypassGroupCheck' => &$bypassGroupCheck];
1797  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_checkEnableFields'] as $_funcRef) {
1798  // Call hooks: If one returns FALSE, method execution is aborted with result "This record is not available"
1799  $return = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1800  if ($return === false) {
1801  return false;
1802  }
1803  }
1804  }
1805  if ((!$row['hidden'] || $this->showHiddenPage) && $row['starttime'] <= $GLOBALS['SIM_ACCESS_TIME'] && ($row['endtime'] == 0 || $row['endtime'] > $GLOBALS['SIM_ACCESS_TIME']) && ($bypassGroupCheck || $this->checkPageGroupAccess($row))) {
1806  return true;
1807  }
1808  return false;
1809  }
1810 
1819  public function checkPageGroupAccess($row, $groupList = null)
1820  {
1821  if (is_null($groupList)) {
1822  $groupList = $this->gr_list;
1823  }
1824  if (!is_array($groupList)) {
1825  $groupList = explode(',', $groupList);
1826  }
1827  $pageGroupList = explode(',', $row['fe_group'] ?: 0);
1828  return count(array_intersect($groupList, $pageGroupList)) > 0;
1829  }
1830 
1839  public function checkPagerecordForIncludeSection($row)
1840  {
1841  return !$row['extendToSubpages'] || $this->checkEnableFields($row) ? 1 : 0;
1842  }
1843 
1850  {
1851  // Initialize:
1852  $c = count($this->rootLine);
1853  $disable = false;
1854  // Traverse root line from root and outwards:
1855  for ($a = 0; $a < $c; $a++) {
1856  // If a value is set for login state:
1857  if ($this->rootLine[$a]['fe_login_mode'] > 0) {
1858  // Determine state from value:
1859  if ((int)$this->rootLine[$a]['fe_login_mode'] === 1) {
1860  $disable = true;
1861  $this->loginAllowedInBranch_mode = 'all';
1862  } elseif ((int)$this->rootLine[$a]['fe_login_mode'] === 3) {
1863  $disable = true;
1864  $this->loginAllowedInBranch_mode = 'groups';
1865  } else {
1866  $disable = false;
1867  }
1868  }
1869  }
1870  return !$disable;
1871  }
1872 
1879  {
1880  $output = [];
1881  $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'] : []);
1882  if (!empty($combinedRecords)) {
1883  foreach ($combinedRecords as $k => $pagerec) {
1884  // 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
1885  // 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!
1886  if (!$k || $pagerec['extendToSubpages']) {
1887  if ($pagerec['hidden']) {
1888  $output['hidden'][$pagerec['uid']] = true;
1889  }
1890  if ($pagerec['starttime'] > $GLOBALS['SIM_ACCESS_TIME']) {
1891  $output['starttime'][$pagerec['uid']] = $pagerec['starttime'];
1892  }
1893  if ($pagerec['endtime'] != 0 && $pagerec['endtime'] <= $GLOBALS['SIM_ACCESS_TIME']) {
1894  $output['endtime'][$pagerec['uid']] = $pagerec['endtime'];
1895  }
1896  if (!$this->checkPageGroupAccess($pagerec)) {
1897  $output['fe_group'][$pagerec['uid']] = $pagerec['fe_group'];
1898  }
1899  }
1900  }
1901  }
1902  return $output;
1903  }
1904 
1913  public function setIDfromArgV()
1914  {
1915  if (!$this->id) {
1916  list($theAlias) = explode('&', GeneralUtility::getIndpEnv('QUERY_STRING'));
1917  $theAlias = trim($theAlias);
1918  $this->id = $theAlias != '' && strpos($theAlias, '=') === false ? $theAlias : 0;
1919  }
1920  }
1921 
1931  {
1932  $this->getPageAndRootline();
1933  // 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.
1934  if ($domainStartPage && is_array($this->rootLine)) {
1935  $idFound = 0;
1936  foreach ($this->rootLine as $key => $val) {
1937  if ($val['uid'] == $domainStartPage) {
1938  $idFound = 1;
1939  break;
1940  }
1941  }
1942  if (!$idFound) {
1943  // Page is 'not found' in case the id was outside the domain, code 3
1944  $this->pageNotFound = 3;
1945  $this->id = $domainStartPage;
1946  // re-get the page and rootline if the id was not found.
1947  $this->getPageAndRootline();
1948  }
1949  }
1950  }
1951 
1958  public function setSysPageWhereClause()
1959  {
1960  $this->sys_page->where_hid_del .= ' AND pages.doktype<200';
1961  $this->sys_page->where_groupAccess = $this->sys_page->getMultipleGroupsWhereClause('pages.fe_group', 'pages');
1962  }
1963 
1971  public function findDomainRecord($recursive = false)
1972  {
1973  if ($recursive) {
1974  $pageUid = 0;
1975  $host = explode('.', GeneralUtility::getIndpEnv('HTTP_HOST'));
1976  while (count($host)) {
1977  $pageUid = $this->sys_page->getDomainStartPage(implode('.', $host), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1978  if ($pageUid) {
1979  return $pageUid;
1980  } else {
1981  array_shift($host);
1982  }
1983  }
1984  return $pageUid;
1985  } else {
1986  return $this->sys_page->getDomainStartPage(GeneralUtility::getIndpEnv('HTTP_HOST'), GeneralUtility::getIndpEnv('SCRIPT_NAME'), GeneralUtility::getIndpEnv('REQUEST_URI'));
1987  }
1988  }
1989 
1997  public function pageUnavailableAndExit($reason = '', $header = '')
1998  {
1999  $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling_statheader'];
2000  $this->pageUnavailableHandler($this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling'], $header, $reason);
2001  die;
2002  }
2003 
2011  public function pageNotFoundAndExit($reason = '', $header = '')
2012  {
2013  $header = $header ?: $this->TYPO3_CONF_VARS['FE']['pageNotFound_handling_statheader'];
2014  $this->pageNotFoundHandler($this->TYPO3_CONF_VARS['FE']['pageNotFound_handling'], $header, $reason);
2015  die;
2016  }
2017 
2025  {
2026  if (
2027  $this->TYPO3_CONF_VARS['FE']['pageUnavailable_handling']
2029  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2030  $this->TYPO3_CONF_VARS['SYS']['devIPmask']
2031  )
2032  ) {
2033  $checkPageUnavailableHandler = true;
2034  } else {
2035  $checkPageUnavailableHandler = false;
2036  }
2037  return $checkPageUnavailableHandler;
2038  }
2039 
2048  public function pageUnavailableHandler($code, $header, $reason)
2049  {
2050  $this->pageErrorHandler($code, $header, $reason);
2051  }
2052 
2061  public function pageNotFoundHandler($code, $header = '', $reason = '')
2062  {
2063  $this->pageErrorHandler($code, $header, $reason);
2064  }
2065 
2076  public function pageErrorHandler($code, $header = '', $reason = '')
2077  {
2078  // Issue header in any case:
2079  if ($header) {
2080  $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2081  foreach ($headerArr as $header) {
2082  header($header);
2083  }
2084  }
2085  // Create response:
2086  // Simply boolean; Just shows TYPO3 error page with reason:
2087  if (gettype($code) == 'boolean' || (string)$code === '1') {
2088  $title = 'Page Not Found';
2089  $message = 'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . htmlspecialchars($reason) : '');
2090  $messagePage = GeneralUtility::makeInstance(ErrorpageMessage::class, $message, $title);
2091  $messagePage->output();
2092  die;
2093  } elseif (GeneralUtility::isFirstPartOfStr($code, 'USER_FUNCTION:')) {
2094  $funcRef = trim(substr($code, 14));
2095  $params = [
2096  'currentUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'),
2097  'reasonText' => $reason,
2098  'pageAccessFailureReasons' => $this->getPageAccessFailureReasons()
2099  ];
2100  echo GeneralUtility::callUserFunction($funcRef, $params, $this);
2101  } elseif (GeneralUtility::isFirstPartOfStr($code, 'READFILE:')) {
2102  $readFile = GeneralUtility::getFileAbsFileName(trim(substr($code, 9)));
2103  if (@is_file($readFile)) {
2104  echo str_replace(
2105  [
2106  '###CURRENT_URL###',
2107  '###REASON###'
2108  ],
2109  [
2110  GeneralUtility::getIndpEnv('REQUEST_URI'),
2111  htmlspecialchars($reason)
2112  ],
2113  GeneralUtility::getUrl($readFile)
2114  );
2115  } else {
2116  throw new \RuntimeException('Configuration Error: 404 page "' . $readFile . '" could not be found.', 1294587214);
2117  }
2118  } elseif (GeneralUtility::isFirstPartOfStr($code, 'REDIRECT:')) {
2119  HttpUtility::redirect(substr($code, 9));
2120  } elseif ($code !== '') {
2121  // Check if URL is relative
2122  $url_parts = parse_url($code);
2123  if ($url_parts['host'] == '') {
2124  $url_parts['host'] = GeneralUtility::getIndpEnv('HTTP_HOST');
2125  if ($code[0] === '/') {
2126  $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST') . $code;
2127  } else {
2128  $code = GeneralUtility::getIndpEnv('TYPO3_REQUEST_DIR') . $code;
2129  }
2130  $checkBaseTag = false;
2131  } else {
2132  $checkBaseTag = true;
2133  }
2134  // Check recursion
2135  if ($code == GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')) {
2136  if ($reason == '') {
2137  $reason = 'Page cannot be found.';
2138  }
2139  $reason .= LF . LF . 'Additionally, ' . $code . ' was not found while trying to retrieve the error document.';
2140  throw new \RuntimeException(nl2br(htmlspecialchars($reason)), 1294587215);
2141  }
2142  // Prepare headers
2143  $headerArr = [
2144  'User-agent: ' . GeneralUtility::getIndpEnv('HTTP_USER_AGENT'),
2145  'Referer: ' . GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')
2146  ];
2147  $res = GeneralUtility::getUrl($code, 1, $headerArr);
2148  // Header and content are separated by an empty line
2149  list($header, $content) = explode(CRLF . CRLF, $res, 2);
2150  // There can be multiple header blocks when using a proxy with cURL
2151  while (substr($content, 0, 4) === 'HTTP') {
2152  list($header, $content) = explode(CRLF . CRLF, $content, 2);
2153  }
2154  $content .= CRLF;
2155  if (false === $res) {
2156  // Last chance -- redirect
2157  HttpUtility::redirect($code);
2158  } else {
2159  // Forward these response headers to the client
2160  $forwardHeaders = [
2161  'Content-Type:'
2162  ];
2163  $headerArr = preg_split('/\\r|\\n/', $header, -1, PREG_SPLIT_NO_EMPTY);
2164  foreach ($headerArr as $header) {
2165  foreach ($forwardHeaders as $h) {
2166  if (preg_match('/^' . $h . '/', $header)) {
2167  header($header);
2168  }
2169  }
2170  }
2171  // Put <base> if necesary
2172  if ($checkBaseTag) {
2173  // If content already has <base> tag, we do not need to do anything
2174  if (false === stristr($content, '<base ')) {
2175  // Generate href for base tag
2176  $base = $url_parts['scheme'] . '://';
2177  if ($url_parts['user'] != '') {
2178  $base .= $url_parts['user'];
2179  if ($url_parts['pass'] != '') {
2180  $base .= ':' . $url_parts['pass'];
2181  }
2182  $base .= '@';
2183  }
2184  $base .= $url_parts['host'];
2185  // Add path portion skipping possible file name
2186  $base .= preg_replace('/(.*\\/)[^\\/]*/', '${1}', $url_parts['path']);
2187  // Put it into content (generate also <head> if necessary)
2188  $replacement = LF . '<base href="' . htmlentities($base) . '" />' . LF;
2189  if (stristr($content, '<head>')) {
2190  $content = preg_replace('/(<head>)/i', '\\1' . $replacement, $content);
2191  } else {
2192  $content = preg_replace('/(<html[^>]*>)/i', '\\1<head>' . $replacement . '</head>', $content);
2193  }
2194  }
2195  }
2196  // Output the content
2197  echo $content;
2198  }
2199  } else {
2200  $title = 'Page Not Found';
2201  $message = $reason ? 'Reason: ' . htmlspecialchars($reason) : 'Page cannot be found.';
2202  $messagePage = GeneralUtility::makeInstance(ErrorpageMessage::class, $message, $title);
2203  $messagePage->output();
2204  }
2205  die;
2206  }
2207 
2215  public function checkAndSetAlias()
2216  {
2217  if ($this->id && !MathUtility::canBeInterpretedAsInteger($this->id)) {
2218  $aid = $this->sys_page->getPageIdFromAlias($this->id);
2219  if ($aid) {
2220  $this->id = $aid;
2221  } else {
2222  $this->pageNotFound = 4;
2223  }
2224  }
2225  }
2226 
2233  public function mergingWithGetVars($GET_VARS)
2234  {
2235  if (is_array($GET_VARS)) {
2236  // Getting $_GET var, unescaped.
2237  $realGet = GeneralUtility::_GET();
2238  if (!is_array($realGet)) {
2239  $realGet = [];
2240  }
2241  // Merge new values on top:
2242  ArrayUtility::mergeRecursiveWithOverrule($realGet, $GET_VARS);
2243  // Write values back to $_GET:
2244  GeneralUtility::_GETset($realGet);
2245  // Setting these specifically (like in the init-function):
2246  if (isset($GET_VARS['type'])) {
2247  $this->type = (int)$GET_VARS['type'];
2248  }
2249  if (isset($GET_VARS['cHash'])) {
2250  $this->cHash = $GET_VARS['cHash'];
2251  }
2252  // @deprecated since TYPO3 7, remove in TYPO3 8 together with jumpurl property.
2253  if (isset($GET_VARS['jumpurl'])) {
2254  $this->jumpurl = $GET_VARS['jumpurl'];
2255  }
2256  if (isset($GET_VARS['MP'])) {
2257  $this->MP = $this->TYPO3_CONF_VARS['FE']['enable_mount_pids'] ? $GET_VARS['MP'] : '';
2258  }
2259  if (isset($GET_VARS['no_cache']) && $GET_VARS['no_cache']) {
2260  $this->set_no_cache('no_cache is requested via GET parameter');
2261  }
2262  }
2263  }
2264 
2265  /********************************************
2266  *
2267  * Template and caching related functions.
2268  *
2269  *******************************************/
2279  public function makeCacheHash()
2280  {
2281  // No need to test anything if caching was already disabled.
2282  if ($this->no_cache && !$this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2283  return;
2284  }
2285  $GET = GeneralUtility::_GET();
2286  if ($this->cHash && is_array($GET)) {
2287  // Make sure we use the page uid and not the page alias
2288  $GET['id'] = $this->id;
2289  $this->cHash_array = $this->cacheHash->getRelevantParameters(GeneralUtility::implodeArrayForUrl('', $GET));
2290  $cHash_calc = $this->cacheHash->calculateCacheHash($this->cHash_array);
2291  if ($cHash_calc != $this->cHash) {
2292  if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2293  $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash comparison failed)');
2294  } else {
2295  $this->disableCache();
2296  $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);
2297  }
2298  }
2299  } elseif (is_array($GET)) {
2300  // No cHash is set, check if that is correct
2301  if ($this->cacheHash->doParametersRequireCacheHash(GeneralUtility::implodeArrayForUrl('', $GET))) {
2302  $this->reqCHash();
2303  }
2304  }
2305  }
2306 
2314  public function reqCHash()
2315  {
2316  if (!$this->cHash) {
2317  if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
2318  if ($this->tempContent) {
2319  $this->clearPageCacheContent();
2320  }
2321  $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash empty)');
2322  } else {
2323  $this->disableCache();
2324  $this->getTimeTracker()->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled', 2);
2325  }
2326  }
2327  }
2328 
2334  public function initTemplate()
2335  {
2336  $this->tmpl = GeneralUtility::makeInstance(TemplateService::class);
2337  $this->tmpl->setVerbose((bool)$this->beUserLogin);
2338  $this->tmpl->init();
2339  $this->tmpl->tt_track = (bool)$this->beUserLogin;
2340  }
2341 
2349  public function getFromCache()
2350  {
2351  // clearing the content-variable, which will hold the pagecontent
2352  $this->content = '';
2353  // Unsetting the lowlevel config
2354  $this->config = [];
2355  $this->cacheContentFlag = false;
2356 
2357  if ($this->no_cache) {
2358  return;
2359  }
2360 
2361  $pageSectionCacheContent = $this->tmpl->getCurrentPageData();
2362  if (!is_array($pageSectionCacheContent)) {
2363  // Nothing in the cache, we acquire an "exclusive lock" for the key now.
2364  // We use the Registry to store this lock centrally,
2365  // but we protect the access again with a global exclusive lock to avoid race conditions
2366 
2367  $this->acquireLock('pagesection', $this->id . '::' . $this->MP);
2368  //
2369  // from this point on we're the only one working on that page ($key)
2370  //
2371 
2372  // query the cache again to see if the page data are there meanwhile
2373  $pageSectionCacheContent = $this->tmpl->getCurrentPageData();
2374  if (is_array($pageSectionCacheContent)) {
2375  // we have the content, nice that some other process did the work for us already
2376  $this->releaseLock('pagesection');
2377  } else {
2378  // We keep the lock set, because we are the ones generating the page now
2379  // and filling the cache.
2380  // This indicates that we have to release the lock in the Registry later in releaseLocks()
2381  }
2382  }
2383 
2384  if (is_array($pageSectionCacheContent)) {
2385  // 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.
2386  // If this hash is not the same in here in this section and after page-generation, then the page will not be properly cached!
2387  // 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.
2388  $pageSectionCacheContent = $this->tmpl->matching($pageSectionCacheContent);
2389  ksort($pageSectionCacheContent);
2390  $this->all = $pageSectionCacheContent;
2391  }
2392  unset($pageSectionCacheContent);
2393 
2394  // Look for page in cache only if a shift-reload is not sent to the server.
2395  $lockHash = $this->getLockHash();
2396  if (!$this->headerNoCache()) {
2397  if ($this->all) {
2398  // we got page section information
2399  $this->newHash = $this->getHash();
2400  $this->getTimeTracker()->push('Cache Row', '');
2401  $row = $this->getFromCache_queryRow();
2402  if (!is_array($row)) {
2403  // nothing in the cache, we acquire an exclusive lock now
2404 
2405  $this->acquireLock('pages', $lockHash);
2406  //
2407  // from this point on we're the only one working on that page ($lockHash)
2408  //
2409 
2410  // query the cache again to see if the data are there meanwhile
2411  $row = $this->getFromCache_queryRow();
2412  if (is_array($row)) {
2413  // we have the content, nice that some other process did the work for us
2414  $this->releaseLock('pages');
2415  } else {
2416  // We keep the lock set, because we are the ones generating the page now
2417  // and filling the cache.
2418  // This indicates that we have to release the lock in the Registry later in releaseLocks()
2419  }
2420  }
2421  if (is_array($row)) {
2422  // we have data from cache
2423 
2424  // Call hook when a page is retrieved from cache:
2425  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'])) {
2426  $_params = ['pObj' => &$this, 'cache_pages_row' => &$row];
2427  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageLoadedFromCache'] as $_funcRef) {
2428  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2429  }
2430  }
2431  // Fetches the lowlevel config stored with the cached data
2432  $this->config = $row['cache_data'];
2433  // Getting the content
2434  $this->content = $row['content'];
2435  // Flag for temp content
2436  $this->tempContent = $row['temp_content'];
2437  // Setting flag, so we know, that some cached content has been loaded
2438  $this->cacheContentFlag = true;
2439  $this->cacheExpires = $row['expires'];
2440 
2441  // Restore page title information, this is needed to generate the page title for
2442  // partially cached pages.
2443  $this->page['title'] = $row['pageTitleInfo']['title'];
2444  $this->altPageTitle = $row['pageTitleInfo']['altPageTitle'];
2445  $this->indexedDocTitle = $row['pageTitleInfo']['indexedDocTitle'];
2446 
2447  if (isset($this->config['config']['debug'])) {
2448  $debugCacheTime = (bool)$this->config['config']['debug'];
2449  } else {
2450  $debugCacheTime = !empty($this->TYPO3_CONF_VARS['FE']['debug']);
2451  }
2452  if ($debugCacheTime) {
2453  $dateFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'];
2454  $timeFormat = $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'];
2455  $this->content .= LF . '<!-- Cached page generated ' . date(($dateFormat . ' ' . $timeFormat), $row['tstamp']) . '. Expires ' . date(($dateFormat . ' ' . $timeFormat), $row['expires']) . ' -->';
2456  }
2457  }
2458  $this->getTimeTracker()->pull();
2459 
2460  return;
2461  }
2462  }
2463  // the user forced rebuilding the page cache or there was no pagesection information
2464  // get a lock for the page content so other processes will not interrupt the regeneration
2465  $this->acquireLock('pages', $lockHash);
2466  }
2467 
2473  public function getFromCache_queryRow()
2474  {
2475  $this->getTimeTracker()->push('Cache Query', '');
2476  $row = $this->pageCache->get($this->newHash);
2477  $this->getTimeTracker()->pull();
2478  return $row;
2479  }
2480 
2488  public function headerNoCache()
2489  {
2490  $disableAcquireCacheData = false;
2491  if ($this->beUserLogin) {
2492  if (strtolower($_SERVER['HTTP_CACHE_CONTROL']) === 'no-cache' || strtolower($_SERVER['HTTP_PRAGMA']) === 'no-cache') {
2493  $disableAcquireCacheData = true;
2494  }
2495  }
2496  // Call hook for possible by-pass of requiring of page cache (for recaching purpose)
2497  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'])) {
2498  $_params = ['pObj' => &$this, 'disableAcquireCacheData' => &$disableAcquireCacheData];
2499  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['headerNoCache'] as $_funcRef) {
2500  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2501  }
2502  }
2503  return $disableAcquireCacheData;
2504  }
2505 
2515  public function getHash()
2516  {
2517  return md5($this->createHashBase(false));
2518  }
2519 
2528  public function getLockHash()
2529  {
2530  $lockHash = $this->createHashBase(true);
2531  return md5($lockHash);
2532  }
2533 
2544  protected function createHashBase($createLockHashBase = false)
2545  {
2546  $hashParameters = [
2547  'id' => (int)$this->id,
2548  'type' => (int)$this->type,
2549  'gr_list' => (string)$this->gr_list,
2550  'MP' => (string)$this->MP,
2551  'cHash' => $this->cHash_array,
2552  'domainStartPage' => $this->domainStartPage
2553  ];
2554  // Include the template information if we shouldn't create a lock hash
2555  if (!$createLockHashBase) {
2556  $hashParameters['all'] = $this->all;
2557  }
2558  // Call hook to influence the hash calculation
2559  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'])) {
2560  $_params = [
2561  'hashParameters' => &$hashParameters,
2562  'createLockHashBase' => $createLockHashBase
2563  ];
2564  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['createHashBase'] as $_funcRef) {
2565  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2566  }
2567  }
2568  return serialize($hashParameters);
2569  }
2570 
2577  public function getConfigArray()
2578  {
2579  // 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
2580  if (empty($this->config) || is_array($this->config['INTincScript']) || $this->forceTemplateParsing) {
2581  $timeTracker = $this->getTimeTracker();
2582  $timeTracker->push('Parse template', '');
2583  // Force parsing, if set?:
2584  $this->tmpl->forceTemplateParsing = $this->forceTemplateParsing;
2585  // Start parsing the TS template. Might return cached version.
2586  $this->tmpl->start($this->rootLine);
2587  $timeTracker->pull();
2588  if ($this->tmpl->loaded) {
2589  $timeTracker->push('Setting the config-array', '');
2590  // toplevel - objArrayName
2591  $this->sPre = $this->tmpl->setup['types.'][$this->type];
2592  $this->pSetup = $this->tmpl->setup[$this->sPre . '.'];
2593  if (!is_array($this->pSetup)) {
2594  $message = 'The page is not configured! [type=' . $this->type . '][' . $this->sPre . '].';
2595  if ($this->checkPageUnavailableHandler()) {
2596  $this->pageUnavailableAndExit($message);
2597  } else {
2598  $explanation = 'This means that there is no TypoScript object of type PAGE with typeNum=' . $this->type . ' configured.';
2599  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2600  throw new ServiceUnavailableException($message . ' ' . $explanation, 1294587217);
2601  }
2602  } else {
2603  if (!isset($this->config['config'])) {
2604  $this->config['config'] = [];
2605  }
2606  // Filling the config-array, first with the main "config." part
2607  if (is_array($this->tmpl->setup['config.'])) {
2608  ArrayUtility::mergeRecursiveWithOverrule($this->tmpl->setup['config.'], $this->config['config']);
2609  $this->config['config'] = $this->tmpl->setup['config.'];
2610  }
2611  // override it with the page/type-specific "config."
2612  if (is_array($this->pSetup['config.'])) {
2613  ArrayUtility::mergeRecursiveWithOverrule($this->config['config'], $this->pSetup['config.']);
2614  }
2615  if ($this->config['config']['typolinkEnableLinksAcrossDomains']) {
2616  $this->config['config']['typolinkCheckRootline'] = true;
2617  }
2618  // Set default values for removeDefaultJS and inlineStyle2TempFile so CSS and JS are externalized if compatversion is higher than 4.0
2619  if (!isset($this->config['config']['removeDefaultJS'])) {
2620  $this->config['config']['removeDefaultJS'] = 'external';
2621  }
2622  if (!isset($this->config['config']['inlineStyle2TempFile'])) {
2623  $this->config['config']['inlineStyle2TempFile'] = 1;
2624  }
2625 
2626  if (!isset($this->config['config']['compressJs'])) {
2627  $this->config['config']['compressJs'] = 0;
2628  }
2629  // Processing for the config_array:
2630  $this->config['rootLine'] = $this->tmpl->rootLine;
2631  $this->config['mainScript'] = trim($this->config['config']['mainScript']) ?: 'index.php';
2632  // Class for render Header and Footer parts
2633  if ($this->pSetup['pageHeaderFooterTemplateFile']) {
2634  $file = $this->tmpl->getFileName($this->pSetup['pageHeaderFooterTemplateFile']);
2635  if ($file) {
2636  $this->pageRenderer->setTemplateFile($file);
2637  }
2638  }
2639  }
2640  $timeTracker->pull();
2641  } else {
2642  if ($this->checkPageUnavailableHandler()) {
2643  $this->pageUnavailableAndExit('No TypoScript template found!');
2644  } else {
2645  $message = 'No TypoScript template found!';
2646  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2647  throw new ServiceUnavailableException($message, 1294587218);
2648  }
2649  }
2650  }
2651 
2652  // No cache
2653  // Set $this->no_cache TRUE if the config.no_cache value is set!
2654  if ($this->config['config']['no_cache']) {
2655  $this->set_no_cache('config.no_cache is set');
2656  }
2657  // Merge GET with defaultGetVars
2658  if (!empty($this->config['config']['defaultGetVars.'])) {
2659  $modifiedGetVars = GeneralUtility::removeDotsFromTS($this->config['config']['defaultGetVars.']);
2661  GeneralUtility::_GETset($modifiedGetVars);
2662  }
2663  // Hook for postProcessing the configuration array
2664  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'])) {
2665  $params = ['config' => &$this->config['config']];
2666  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['configArrayPostProc'] as $funcRef) {
2667  GeneralUtility::callUserFunction($funcRef, $params, $this);
2668  }
2669  }
2670  }
2671 
2672  /********************************************
2673  *
2674  * Further initialization and data processing
2675  * (jumpurl/submission of forms)
2676  *
2677  *******************************************/
2678 
2686  public function settingLanguage()
2687  {
2688  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'])) {
2689  $_params = [];
2690  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_preProcess'] as $_funcRef) {
2691  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2692  }
2693  }
2694 
2695  // Initialize charset settings etc.
2696  $this->initLLvars();
2697 
2698  // Get values from TypoScript:
2699  $this->sys_language_uid = ($this->sys_language_content = (int)$this->config['config']['sys_language_uid']);
2700  list($this->sys_language_mode, $sys_language_content) = GeneralUtility::trimExplode(';', $this->config['config']['sys_language_mode']);
2701  $this->sys_language_contentOL = $this->config['config']['sys_language_overlay'];
2702  // If sys_language_uid is set to another language than default:
2703  if ($this->sys_language_uid > 0) {
2704  // check whether a shortcut is overwritten by a translated page
2705  // we can only do this now, as this is the place where we get
2706  // to know about translations
2707  $this->checkTranslatedShortcut();
2708  // Request the overlay record for the sys_language_uid:
2709  $olRec = $this->sys_page->getPageOverlay($this->id, $this->sys_language_uid);
2710  if (empty($olRec)) {
2711  // If no OL record exists and a foreign language is asked for...
2712  if ($this->sys_language_uid) {
2713  // If requested translation is not available:
2714  if (GeneralUtility::hideIfNotTranslated($this->page['l18n_cfg'])) {
2715  $this->pageNotFoundAndExit('Page is not available in the requested language.');
2716  } else {
2717  switch ((string)$this->sys_language_mode) {
2718  case 'strict':
2719  $this->pageNotFoundAndExit('Page is not available in the requested language (strict).');
2720  break;
2721  case 'content_fallback':
2722  $fallBackOrder = GeneralUtility::intExplode(',', $sys_language_content);
2723  foreach ($fallBackOrder as $orderValue) {
2724  if ((string)$orderValue === '0' || !empty($this->sys_page->getPageOverlay($this->id, $orderValue))) {
2725  $this->sys_language_content = $orderValue;
2726  // Setting content uid (but leaving the sys_language_uid)
2727  break;
2728  }
2729  }
2730  break;
2731  case 'ignore':
2732  $this->sys_language_content = $this->sys_language_uid;
2733  break;
2734  default:
2735  // Default is that everything defaults to the default language...
2736  $this->sys_language_uid = ($this->sys_language_content = 0);
2737  }
2738  }
2739  }
2740  } else {
2741  // Setting sys_language if an overlay record was found (which it is only if a language is used)
2742  $this->page = $this->sys_page->getPageOverlay($this->page, $this->sys_language_uid);
2743  }
2744  }
2745  // Setting sys_language_uid inside sys-page:
2746  $this->sys_page->sys_language_uid = $this->sys_language_uid;
2747  // If default translation is not available:
2748  if ((!$this->sys_language_uid || !$this->sys_language_content) && GeneralUtility::hideIfDefaultLanguage($this->page['l18n_cfg'])) {
2749  $message = 'Page is not available in default language.';
2750  GeneralUtility::sysLog($message, 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
2751  $this->pageNotFoundAndExit($message);
2752  }
2754 
2755  // Finding the ISO code for the currently selected language
2756  // fetched by the sys_language record when not fetching content from the default language
2757  if ($this->sys_language_content > 0) {
2758  // using sys_language_content because the ISO code only (currently) affect content selection from FlexForms - which should follow "sys_language_content"
2759  // Set the fourth parameter to TRUE in the next two getRawRecord() calls to
2760  // avoid versioning overlay to be applied as it generates an SQL error
2761  $sys_language_row = $this->sys_page->getRawRecord('sys_language', $this->sys_language_content, 'language_isocode,static_lang_isocode', true);
2762  if (is_array($sys_language_row)) {
2763  if (!empty($sys_language_row['language_isocode'])) {
2764  $this->sys_language_isocode = $sys_language_row['language_isocode'];
2765  } elseif ($sys_language_row['static_lang_isocode'] && ExtensionManagementUtility::isLoaded('static_info_tables')) {
2766  GeneralUtility::deprecationLog('Usage of the field "static_lang_isocode" is discouraged, and will stop working with CMS 8. Use the built-in language field "language_isocode" in your sys_language records.');
2767  $stLrow = $this->sys_page->getRawRecord('static_languages', $sys_language_row['static_lang_isocode'], 'lg_iso_2', true);
2768  $this->sys_language_isocode = $stLrow['lg_iso_2'];
2769  }
2770  }
2771  // the DB value is overriden by TypoScript
2772  if (!empty($this->config['config']['sys_language_isocode'])) {
2773  $this->sys_language_isocode = $this->config['config']['sys_language_isocode'];
2774  }
2775  } else {
2776  // fallback to the TypoScript option when rendering with sys_language_uid=0
2777  // also: use "en" by default
2778  if (!empty($this->config['config']['sys_language_isocode_default'])) {
2779  $this->sys_language_isocode = $this->config['config']['sys_language_isocode_default'];
2780  } else {
2781  $this->sys_language_isocode = $this->lang != 'default' ? $this->lang : 'en';
2782  }
2783  }
2784 
2785  // Setting softMergeIfNotBlank:
2786  $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softMergeIfNotBlank'], true);
2787  foreach ($table_fields as $TF) {
2788  list($tN, $fN) = explode(':', $TF);
2789  $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'mergeIfNotBlank';
2790  }
2791  // Setting softExclude:
2792  $table_fields = GeneralUtility::trimExplode(',', $this->config['config']['sys_language_softExclude'], true);
2793  foreach ($table_fields as $TF) {
2794  list($tN, $fN) = explode(':', $TF);
2795  $GLOBALS['TCA'][$tN]['columns'][$fN]['l10n_mode'] = 'exclude';
2796  }
2797  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'])) {
2798  $_params = [];
2799  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['settingLanguage_postProcess'] as $_funcRef) {
2800  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
2801  }
2802  }
2803  }
2804 
2808  protected function updateRootLinesWithTranslations()
2809  {
2810  if ($this->sys_language_uid) {
2811  $this->rootLine = $this->sys_page->getRootLine($this->id, $this->MP);
2812  $this->tmpl->updateRootlineData($this->rootLine);
2813  }
2814  }
2815 
2821  public function settingLocale()
2822  {
2823  // Setting locale
2824  if ($this->config['config']['locale_all']) {
2825  // If LC_NUMERIC is set e.g. to 'de_DE' PHP parses float values locale-aware resulting in strings with comma
2826  // as decimal point which causes problems with value conversions - so we set all locale types except LC_NUMERIC
2827  // @see https://bugs.php.net/bug.php?id=53711
2828  $locale = setlocale(LC_COLLATE, $this->config['config']['locale_all']);
2829  if ($locale) {
2830  // As str_* methods are locale aware and turkish has no upper case I
2831  // Class autoloading and other checks depending on case changing break with turkish locale LC_CTYPE
2832  // @see http://bugs.php.net/bug.php?id=35050
2833  if (substr($this->config['config']['locale_all'], 0, 2) != 'tr') {
2834  setlocale(LC_CTYPE, $this->config['config']['locale_all']);
2835  }
2836  setlocale(LC_MONETARY, $this->config['config']['locale_all']);
2837  setlocale(LC_TIME, $this->config['config']['locale_all']);
2838  $this->localeCharset = $this->csConvObj->get_locale_charset($this->config['config']['locale_all']);
2839  } else {
2840  $this->getTimeTracker()->setTSlogMessage('Locale "' . htmlspecialchars($this->config['config']['locale_all']) . '" not found.', 3);
2841  }
2842  }
2843  }
2844 
2853  protected function checkTranslatedShortcut()
2854  {
2855  if (!is_null($this->originalShortcutPage)) {
2856  $originalShortcutPageOverlay = $this->sys_page->getPageOverlay($this->originalShortcutPage['uid'], $this->sys_language_uid);
2857  if (!empty($originalShortcutPageOverlay['shortcut']) && $originalShortcutPageOverlay['shortcut'] != $this->id) {
2858  // the translation of the original shortcut page has a different shortcut target!
2859  // set the correct page and id
2860  $shortcut = $this->getPageShortcut($originalShortcutPageOverlay['shortcut'], $originalShortcutPageOverlay['shortcut_mode'], $originalShortcutPageOverlay['uid']);
2861  $this->id = ($this->contentPid = $shortcut['uid']);
2862  $this->page = $this->sys_page->getPage($this->id);
2863  // Fix various effects on things like menus f.e.
2864  $this->fetch_the_id();
2865  $this->tmpl->rootLine = array_reverse($this->rootLine);
2866  }
2867  }
2868  }
2869 
2876  public function handleDataSubmission()
2877  {
2878  // Hook for processing data submission to extensions
2879  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'])) {
2880  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['checkDataSubmission'] as $_classRef) {
2881  $_procObj = GeneralUtility::getUserObj($_classRef);
2882  $_procObj->checkDataSubmission($this);
2883  }
2884  }
2885  }
2886 
2892  public function initializeRedirectUrlHandlers()
2893  {
2894  if (
2895  empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'])
2896  || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'])
2897  ) {
2898  return;
2899  }
2900 
2901  $urlHandlers = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlHandlers'];
2902  foreach ($urlHandlers as $identifier => $configuration) {
2903  if (empty($configuration) || !is_array($configuration)) {
2904  throw new \RuntimeException('Missing configuration for URL handler "' . $identifier . '".', 1442052263);
2905  }
2906  if (!is_string($configuration['handler']) || empty($configuration['handler']) || !class_exists($configuration['handler']) || !is_subclass_of($configuration['handler'], UrlHandlerInterface::class)) {
2907  throw new \RuntimeException('The URL handler "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlHandlerInterface::class . '".', 1442052249);
2908  }
2909  }
2910 
2911  $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlHandlers);
2912 
2913  foreach ($orderedHandlers as $configuration) {
2915  $urlHandler = GeneralUtility::makeInstance($configuration['handler']);
2916  if ($urlHandler->canHandleCurrentUrl()) {
2917  $this->activeUrlHandlers[] = $urlHandler;
2918  }
2919  }
2920  }
2921 
2929  public function setExternalJumpUrl()
2930  {
2932  $this->initializeRedirectUrlHandlers();
2933  }
2934 
2945  public function jumpUrl()
2946  {
2948  $this->redirectToExternalUrl();
2949  }
2950 
2959  public function redirectToExternalUrl()
2960  {
2961  foreach ($this->activeUrlHandlers as $redirectHandler) {
2962  $redirectHandler->handle();
2963  }
2964 
2965  if (!empty($this->activeUrlHandlers)) {
2966  throw new \RuntimeException('A URL handler is active but did not process the URL.', 1442305505);
2967  }
2968  }
2969 
2977  public function setUrlIdToken()
2978  {
2979  if ($this->config['config']['ftu']) {
2980  $this->getMethodUrlIdToken = $this->TYPO3_CONF_VARS['FE']['get_url_id_token'];
2981  } else {
2982  $this->getMethodUrlIdToken = '';
2983  }
2984  }
2985 
2992  public function calculateLinkVars()
2993  {
2994  $this->linkVars = '';
2995  $linkVars = GeneralUtility::trimExplode(',', (string)$this->config['config']['linkVars']);
2996  if (empty($linkVars)) {
2997  return;
2998  }
2999  $getData = GeneralUtility::_GET();
3000  foreach ($linkVars as $linkVar) {
3001  $test = ($value = '');
3002  if (preg_match('/^(.*)\\((.+)\\)$/', $linkVar, $match)) {
3003  $linkVar = trim($match[1]);
3004  $test = trim($match[2]);
3005  }
3006  if ($linkVar === '' || !isset($getData[$linkVar])) {
3007  continue;
3008  }
3009  if (!is_array($getData[$linkVar])) {
3010  $temp = rawurlencode($getData[$linkVar]);
3011  if ($test !== '' && !PageGenerator::isAllowedLinkVarValue($temp, $test)) {
3012  // Error: This value was not allowed for this key
3013  continue;
3014  }
3015  $value = '&' . $linkVar . '=' . $temp;
3016  } else {
3017  if ($test !== '' && $test !== 'array') {
3018  // Error: This key must not be an array!
3019  continue;
3020  }
3021  $value = GeneralUtility::implodeArrayForUrl($linkVar, $getData[$linkVar]);
3022  }
3023  $this->linkVars .= $value;
3024  }
3025  }
3026 
3036  {
3037  if (!empty($this->originalMountPointPage) && $this->originalMountPointPage['doktype'] == PageRepository::DOKTYPE_MOUNTPOINT) {
3038  $this->redirectToCurrentPage();
3039  }
3040  }
3041 
3051  {
3052  if (!empty($this->originalShortcutPage) && $this->originalShortcutPage['doktype'] == PageRepository::DOKTYPE_SHORTCUT) {
3053  $this->redirectToCurrentPage();
3054  }
3055  }
3056 
3063  protected function redirectToCurrentPage()
3064  {
3065  $this->calculateLinkVars();
3066  // Instantiate \TYPO3\CMS\Frontend\ContentObject to generate the correct target URL
3068  $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
3069  $parameter = $this->page['uid'];
3070  $type = GeneralUtility::_GET('type');
3072  $parameter .= ',' . $type;
3073  }
3074  $redirectUrl = $cObj->typoLink_URL(['parameter' => $parameter]);
3075 
3076  // Prevent redirection loop
3077  if (!empty($redirectUrl)) {
3078  // redirect and exit
3080  }
3081  }
3082 
3083  /********************************************
3084  *
3085  * Page generation; cache handling
3086  *
3087  *******************************************/
3094  public function isGeneratePage()
3095  {
3096  return !$this->cacheContentFlag && empty($this->activeUrlHandlers);
3097  }
3098 
3105  public function tempPageCacheContent()
3106  {
3107  $this->tempContent = false;
3108  if (!$this->no_cache) {
3109  $seconds = 30;
3110  $title = htmlspecialchars($this->tmpl->printTitle($this->page['title']));
3111  $request_uri = htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI'));
3112  $stdMsg = '
3113  <strong>Page is being generated.</strong><br />
3114  If this message does not disappear within ' . $seconds . ' seconds, please reload.';
3115  $message = $this->config['config']['message_page_is_being_generated'];
3116  if ((string)$message !== '') {
3117  // This page is always encoded as UTF-8
3118  $message = $this->csConvObj->utf8_encode($message, $this->renderCharset);
3119  $message = str_replace('###TITLE###', $title, $message);
3120  $message = str_replace('###REQUEST_URI###', $request_uri, $message);
3121  } else {
3122  $message = $stdMsg;
3123  }
3124  $temp_content = '<?xml version="1.0" encoding="UTF-8"?>
3125 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3126  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3127 <html xmlns="http://www.w3.org/1999/xhtml">
3128  <head>
3129  <title>' . $title . '</title>
3130  <meta http-equiv="refresh" content="10" />
3131  </head>
3132  <body style="background-color:white; font-family:Verdana,Arial,Helvetica,sans-serif; color:#cccccc; text-align:center;">' . $message . '
3133  </body>
3134 </html>';
3135  // Fix 'nice errors' feature in modern browsers
3136  $padSuffix = '<!--pad-->';
3137  // prevent any trims
3138  $padSize = 768 - strlen($padSuffix) - strlen($temp_content);
3139  if ($padSize > 0) {
3140  $temp_content = str_pad($temp_content, $padSize, LF) . $padSuffix;
3141  }
3142  if (!$this->headerNoCache() && ($cachedRow = $this->getFromCache_queryRow())) {
3143  // 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.
3144  // This is either the "Page is being generated" screen or it can be the final result.
3145  // 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.
3146  // 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...
3147  $this->set_no_cache('Another process wrote into the cache since the beginning of the render process', true);
3148 
3149  // Since the new Locking API this should never be the case
3150  } else {
3151  $this->tempContent = true;
3152  // This flag shows that temporary content is put in the cache
3153  $this->setPageCacheContent($temp_content, $this->config, $GLOBALS['EXEC_TIME'] + $seconds);
3154  }
3155  }
3156  }
3157 
3163  public function realPageCacheContent()
3164  {
3165  // seconds until a cached page is too old
3166  $cacheTimeout = $this->get_cache_timeout();
3167  $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
3168  $this->tempContent = false;
3169  $usePageCache = true;
3170  // Hook for deciding whether page cache should be written to the cache backend or not
3171  // NOTE: as hooks are called in a loop, the last hook will have the final word (however each
3172  // hook receives the current status of the $usePageCache flag)
3173  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache'])) {
3174  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['usePageCache'] as $_classRef) {
3175  $_procObj = GeneralUtility::getUserObj($_classRef);
3176  $usePageCache = $_procObj->usePageCache($this, $usePageCache);
3177  }
3178  }
3179  // Write the page to cache, if necessary
3180  if ($usePageCache) {
3181  $this->setPageCacheContent($this->content, $this->config, $timeOutTime);
3182  }
3183  // Hook for cache post processing (eg. writing static files!)
3184  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache'])) {
3185  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['insertPageIncache'] as $_classRef) {
3186  $_procObj = GeneralUtility::getUserObj($_classRef);
3187  $_procObj->insertPageIncache($this, $timeOutTime);
3188  }
3189  }
3190  }
3191 
3201  public function setPageCacheContent($content, $data, $expirationTstamp)
3202  {
3203  $cacheData = [
3204  'identifier' => $this->newHash,
3205  'page_id' => $this->id,
3206  'content' => $content,
3207  'temp_content' => $this->tempContent,
3208  'cache_data' => $data,
3209  'expires' => $expirationTstamp,
3210  'tstamp' => $GLOBALS['EXEC_TIME'],
3211  'pageTitleInfo' => [
3212  'title' => $this->page['title'],
3213  'altPageTitle' => $this->altPageTitle,
3214  'indexedDocTitle' => $this->indexedDocTitle
3215  ]
3216  ];
3217  $this->cacheExpires = $expirationTstamp;
3218  $this->pageCacheTags[] = 'pageId_' . $cacheData['page_id'];
3219  if ($this->page_cache_reg1) {
3220  $reg1 = (int)$this->page_cache_reg1;
3221  $cacheData['reg1'] = $reg1;
3222  $this->pageCacheTags[] = 'reg1_' . $reg1;
3223  }
3224  if (!empty($this->page['cache_tags'])) {
3225  $tags = GeneralUtility::trimExplode(',', $this->page['cache_tags'], true);
3226  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
3227  }
3228  $this->pageCache->set($this->newHash, $cacheData, $this->pageCacheTags, $expirationTstamp - $GLOBALS['EXEC_TIME']);
3229  }
3230 
3236  public function clearPageCacheContent()
3237  {
3238  $this->pageCache->remove($this->newHash);
3239  }
3240 
3247  public function clearPageCacheContent_pidList($pidList)
3248  {
3249  $pageIds = GeneralUtility::trimExplode(',', $pidList);
3250  foreach ($pageIds as $pageId) {
3251  $this->pageCache->flushByTag('pageId_' . (int)$pageId);
3252  }
3253  }
3254 
3262  public function setSysLastChanged()
3263  {
3264  // Draft workspaces are always uid 1 or more. We do not update SYS_LASTCHANGED if we are browsing page from one of theses workspaces
3265  if ((int)$this->whichWorkspace() < 1 && $this->page['SYS_LASTCHANGED'] < (int)$this->register['SYS_LASTCHANGED']) {
3266  $this->getDatabaseConnection()->exec_UPDATEquery('pages', 'uid=' . (int)$this->id, ['SYS_LASTCHANGED' => (int)$this->register['SYS_LASTCHANGED']]);
3267  }
3268  }
3269 
3280  public function acquirePageGenerationLock(&$lockObj, $key)
3281  {
3283  if ($this->no_cache || $this->headerNoCache()) {
3284  GeneralUtility::sysLog('Locking: Page is not cached, no locking required', 'cms', GeneralUtility::SYSLOG_SEVERITY_INFO);
3285  // No locking is needed if caching is disabled
3286  return true;
3287  }
3288  try {
3289  if (!is_object($lockObj)) {
3290  $lockObj = GeneralUtility::makeInstance(Locker::class, $key, $this->TYPO3_CONF_VARS['SYS']['lockingMode']);
3291  }
3292  $success = false;
3293  if ($key !== '') {
3294  // TRUE = Page could get locked without blocking
3295  // FALSE = Page could get locked but process was blocked before
3296  $success = $lockObj->acquire();
3297  if ($lockObj->getLockStatus()) {
3298  $lockObj->sysLog('Acquired lock');
3299  }
3300  }
3301  } catch (\Exception $e) {
3302  GeneralUtility::sysLog('Locking: Failed to acquire lock: ' . $e->getMessage(), 'cms', GeneralUtility::SYSLOG_SEVERITY_ERROR);
3303  // If locking fails, return with FALSE and continue without locking
3304  $success = false;
3305  }
3306  return $success;
3307  }
3308 
3317  public function releasePageGenerationLock(&$lockObj)
3318  {
3320  $success = false;
3321  // If lock object is set and was acquired (may also happen if no_cache was enabled during runtime), release it:
3322  if (is_object($lockObj) && $lockObj instanceof Locker && $lockObj->getLockStatus()) {
3323  $success = $lockObj->release();
3324  $lockObj->sysLog('Released lock');
3325  $lockObj = null;
3326  } elseif ($this->no_cache || $this->headerNoCache()) {
3327  $success = true;
3328  }
3329  return $success;
3330  }
3331 
3338  public function releaseLocks()
3339  {
3340  $this->releaseLock('pagesection');
3341  $this->releaseLock('pages');
3342  }
3343 
3351  public function addCacheTags(array $tags)
3352  {
3353  $this->pageCacheTags = array_merge($this->pageCacheTags, $tags);
3354  }
3355 
3356  /********************************************
3357  *
3358  * Page generation; rendering and inclusion
3359  *
3360  *******************************************/
3366  public function generatePage_preProcessing()
3367  {
3368  // Same codeline as in getFromCache(). But $this->all has been changed by
3369  // \TYPO3\CMS\Core\TypoScript\TemplateService::start() in the meantime, so this must be called again!
3370  $this->newHash = $this->getHash();
3371 
3372  // If the pages_lock is set, we are in charge of generating the page.
3373  if (is_object($this->locks['pages']['accessLock'])) {
3374  // Here we put some temporary stuff in the cache in order to let the first hit generate the page.
3375  // The temporary cache will expire after a few seconds (typ. 30) or will be cleared by the rendered page,
3376  // which will also clear and rewrite the cache.
3377  $this->tempPageCacheContent();
3378  }
3379  // At this point we have a valid pagesection_cache and also some temporary page_cache content,
3380  // so let all other processes proceed now. (They are blocked at the pagessection_lock in getFromCache())
3381  $this->releaseLock('pagesection');
3382 
3383  // Setting cache_timeout_default. May be overridden by PHP include scripts.
3384  $this->cacheTimeOutDefault = (int)$this->config['config']['cache_period'];
3385  // Page is generated
3386  $this->no_cacheBeforePageGen = $this->no_cache;
3387  }
3388 
3395  public function generatePage_whichScript()
3396  {
3397  if (!$this->TYPO3_CONF_VARS['FE']['noPHPscriptInclude'] && $this->config['config']['pageGenScript']) {
3398  return $this->tmpl->getFileName($this->config['config']['pageGenScript']);
3399  }
3400  return null;
3401  }
3402 
3410  {
3411  // 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.
3412  if ($this->no_cacheBeforePageGen) {
3413  $this->set_no_cache('no_cache has been set before the page was generated - safety check', true);
3414  }
3415  // Hook for post-processing of page content cached/non-cached:
3416  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'])) {
3417  $_params = ['pObj' => &$this];
3418  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-all'] as $_funcRef) {
3419  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3420  }
3421  }
3422  // Processing if caching is enabled:
3423  if (!$this->no_cache) {
3424  // Hook for post-processing of page content before being cached:
3425  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached'])) {
3426  $_params = ['pObj' => &$this];
3427  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-cached'] as $_funcRef) {
3428  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3429  }
3430  }
3431  }
3432  // Convert char-set for output: (should be BEFORE indexing of the content (changed 22/4 2005)),
3433  // because otherwise indexed search might convert from the wrong charset!
3434  // One thing is that the charset mentioned in the HTML header would be wrong since the output charset (metaCharset)
3435  // has not been converted to from renderCharset. And indexed search will internally convert from metaCharset
3436  // to renderCharset so the content MUST be in metaCharset already!
3437  $this->content = $this->convOutputCharset($this->content, 'mainpage');
3438  // Hook for indexing pages
3439  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageIndexing'])) {
3440  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['pageIndexing'] as $_classRef) {
3441  $_procObj = GeneralUtility::getUserObj($_classRef);
3442  $_procObj->hook_indexContent($this);
3443  }
3444  }
3445  // Storing for cache:
3446  if (!$this->no_cache) {
3447  $this->realPageCacheContent();
3448  } elseif ($this->tempContent) {
3449  // 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)
3450  $this->clearPageCacheContent();
3451  $this->tempContent = false;
3452  }
3453  // Sets sys-last-change:
3454  $this->setSysLastChanged();
3455  }
3456 
3462  protected function regeneratePageTitle()
3463  {
3464  PageGenerator::generatePageTitle();
3465  }
3466 
3472  public function INTincScript()
3473  {
3474  // Deprecated stuff:
3475  // @deprecated: annotation added TYPO3 4.6
3476  $this->additionalHeaderData = is_array($this->config['INTincScript_ext']['additionalHeaderData']) ? $this->config['INTincScript_ext']['additionalHeaderData'] : [];
3477  $this->additionalFooterData = is_array($this->config['INTincScript_ext']['additionalFooterData']) ? $this->config['INTincScript_ext']['additionalFooterData'] : [];
3478  $this->additionalJavaScript = $this->config['INTincScript_ext']['additionalJavaScript'];
3479  $this->additionalCSS = $this->config['INTincScript_ext']['additionalCSS'];
3480  $this->divSection = '';
3481  if (empty($this->config['INTincScript_ext']['pageRenderer'])) {
3482  $this->initPageRenderer();
3483  } else {
3485  $pageRenderer = unserialize($this->config['INTincScript_ext']['pageRenderer']);
3486  $this->pageRenderer = $pageRenderer;
3488  }
3489 
3491  $this->getTimeTracker()->push('Substitute header section');
3492  $this->INTincScript_loadJSCode();
3493  $this->regeneratePageTitle();
3494 
3495  $this->content = str_replace(
3496  [
3497  '<!--HD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
3498  '<!--FD_' . $this->config['INTincScript_ext']['divKey'] . '-->',
3499  '<!--TDS_' . $this->config['INTincScript_ext']['divKey'] . '-->'
3500  ],
3501  [
3502  $this->convOutputCharset(implode(LF, $this->additionalHeaderData), 'HD'),
3503  $this->convOutputCharset(implode(LF, $this->additionalFooterData), 'FD'),
3504  $this->convOutputCharset($this->divSection, 'TDS'),
3505  ],
3506  $this->pageRenderer->renderJavaScriptAndCssForProcessingOfUncachedContentObjects($this->content, $this->config['INTincScript_ext']['divKey'])
3507  );
3508  // Replace again, because header and footer data and page renderer replacements may introduce additional placeholders (see #44825)
3510  $this->setAbsRefPrefix();
3511  $this->getTimeTracker()->pull();
3512  }
3513 
3520  {
3521  do {
3522  $INTiS_config = $this->config['INTincScript'];
3523  $this->INTincScript_includeLibs($INTiS_config);
3524  $this->INTincScript_process($INTiS_config);
3525  // Check if there were new items added to INTincScript during the previous execution:
3526  $INTiS_config = array_diff_assoc($this->config['INTincScript'], $INTiS_config);
3527  $reprocess = count($INTiS_config) > 0;
3528  } while ($reprocess);
3529  }
3530 
3538  protected function INTincScript_includeLibs($INTiS_config)
3539  {
3540  foreach ($INTiS_config as $INTiS_cPart) {
3541  if (isset($INTiS_cPart['conf']['includeLibs']) && $INTiS_cPart['conf']['includeLibs']) {
3542  $INTiS_resourceList = GeneralUtility::trimExplode(',', $INTiS_cPart['conf']['includeLibs'], true);
3543  $this->includeLibraries($INTiS_resourceList);
3544  }
3545  }
3546  }
3547 
3555  protected function INTincScript_process($INTiS_config)
3556  {
3557  $timeTracker = $this->getTimeTracker();
3558  $timeTracker->push('Split content');
3559  // Splits content with the key.
3560  $INTiS_splitC = explode('<!--INT_SCRIPT.', $this->content);
3561  $this->content = '';
3562  $timeTracker->setTSlogMessage('Parts: ' . count($INTiS_splitC));
3563  $timeTracker->pull();
3564  foreach ($INTiS_splitC as $INTiS_c => $INTiS_cPart) {
3565  // If the split had a comment-end after 32 characters it's probably a split-string
3566  if (substr($INTiS_cPart, 32, 3) === '-->') {
3567  $INTiS_key = 'INT_SCRIPT.' . substr($INTiS_cPart, 0, 32);
3568  if (is_array($INTiS_config[$INTiS_key])) {
3569  $timeTracker->push('Include ' . $INTiS_config[$INTiS_key]['file'], '');
3570  $incContent = '';
3571  $INTiS_cObj = unserialize($INTiS_config[$INTiS_key]['cObj']);
3572  /* @var $INTiS_cObj ContentObjectRenderer */
3573  $INTiS_cObj->INT_include = 1;
3574  switch ($INTiS_config[$INTiS_key]['type']) {
3575  case 'COA':
3576  $incContent = $INTiS_cObj->cObjGetSingle('COA', $INTiS_config[$INTiS_key]['conf']);
3577  break;
3578  case 'FUNC':
3579  $incContent = $INTiS_cObj->cObjGetSingle('USER', $INTiS_config[$INTiS_key]['conf']);
3580  break;
3581  case 'POSTUSERFUNC':
3582  $incContent = $INTiS_cObj->callUserFunction($INTiS_config[$INTiS_key]['postUserFunc'], $INTiS_config[$INTiS_key]['conf'], $INTiS_config[$INTiS_key]['content']);
3583  break;
3584  }
3585  $this->content .= $this->convOutputCharset($incContent, 'INC-' . $INTiS_c);
3586  $this->content .= substr($INTiS_cPart, 35);
3587  $timeTracker->pull($incContent);
3588  } else {
3589  $this->content .= substr($INTiS_cPart, 35);
3590  }
3591  } else {
3592  $this->content .= ($INTiS_c ? '<!--INT_SCRIPT.' : '') . $INTiS_cPart;
3593  }
3594  }
3595  }
3596 
3602  public function INTincScript_loadJSCode()
3603  {
3604  // Add javascript
3605  $jsCode = trim($this->JSCode);
3606  $additionalJavaScript = is_array($this->additionalJavaScript)
3607  ? implode(LF, $this->additionalJavaScript)
3610  if ($jsCode !== '' || $additionalJavaScript !== '') {
3611  $this->additionalHeaderData['JSCode'] = '
3612 <script type="text/javascript">
3613  /*<![CDATA[*/
3614 <!--
3615 ' . $additionalJavaScript . '
3616 ' . $jsCode . '
3617 // -->
3618  /*]]>*/
3619 </script>';
3620  }
3621  // Add CSS
3622  $additionalCss = is_array($this->additionalCSS) ? implode(LF, $this->additionalCSS) : $this->additionalCSS;
3623  $additionalCss = trim($additionalCss);
3624  if ($additionalCss !== '') {
3625  $this->additionalHeaderData['_CSS'] = '
3626 <style type="text/css">
3627 ' . $additionalCss . '
3628 </style>';
3629  }
3630  }
3631 
3637  public function isINTincScript()
3638  {
3639  return is_array($this->config['INTincScript']) && empty($this->activeUrlHandlers);
3640  }
3641 
3648  public function doXHTML_cleaning()
3649  {
3651  return $this->config['config']['xhtml_cleaning'];
3652  }
3653 
3660  public function doLocalAnchorFix()
3661  {
3663  return isset($this->config['config']['prefixLocalAnchors']) ? $this->config['config']['prefixLocalAnchors'] : null;
3664  }
3665 
3666  /********************************************
3667  *
3668  * Finished off; outputting, storing session data, statistics...
3669  *
3670  *******************************************/
3677  public function isOutputting()
3678  {
3679  // Initialize by status if there is a Redirect URL
3680  $enableOutput = empty($this->activeUrlHandlers);
3681  // Call hook for possible disabling of output:
3682  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting'])) {
3683  $_params = ['pObj' => &$this, 'enableOutput' => &$enableOutput];
3684  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['isOutputting'] as $_funcRef) {
3685  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3686  }
3687  }
3688  return $enableOutput;
3689  }
3690 
3700  public function processOutput()
3701  {
3702  // Set header for charset-encoding unless disabled
3703  if (empty($this->config['config']['disableCharsetHeader'])) {
3704  $headLine = 'Content-Type: ' . $this->contentType . '; charset=' . trim($this->metaCharset);
3705  header($headLine);
3706  }
3707  // Set header for content language unless disabled
3708  if (empty($this->config['config']['disableLanguageHeader']) && !empty($this->sys_language_isocode)) {
3709  $headLine = 'Content-Language: ' . trim($this->sys_language_isocode);
3710  header($headLine);
3711  }
3712  // Set cache related headers to client (used to enable proxy / client caching!)
3713  if (!empty($this->config['config']['sendCacheHeaders'])) {
3714  $this->sendCacheHeaders();
3715  }
3716  // Set headers, if any
3717  if (!empty($this->config['config']['additionalHeaders'])) {
3718  $headerArray = explode('|', $this->config['config']['additionalHeaders']);
3719  GeneralUtility::deprecationLog('The TypoScript option "config.additionalHeaders" has been deprecated with TYPO3 CMS 7, and will be removed with CMS 8, please use the more flexible syntax config.additionalHeaders.10... to separate each header value.');
3720  foreach ($headerArray as $headLine) {
3721  $headLine = trim($headLine);
3722  header($headLine);
3723  }
3724  }
3725  if (is_array($this->config['config']['additionalHeaders.'])) {
3726  ksort($this->config['config']['additionalHeaders.']);
3727  foreach ($this->config['config']['additionalHeaders.'] as $options) {
3728  header(
3729  trim($options['header']),
3730  // "replace existing headers" is turned on by default, unless turned off
3731  ($options['replace'] !== '0'),
3732  ((int)$options['httpResponseCode'] ?: null)
3733  );
3734  }
3735  }
3736  // Send appropriate status code in case of temporary content
3737  if ($this->tempContent) {
3738  $this->addTempContentHttpHeaders();
3739  }
3740  // Make substitution of eg. username/uid in content only if cache-headers for client/proxy caching is NOT sent!
3741  if (!$this->isClientCachable) {
3742  $this->contentStrReplace();
3743  }
3744  // Hook for post-processing of page content before output:
3745  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output'])) {
3746  $_params = ['pObj' => &$this];
3747  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['contentPostProc-output'] as $_funcRef) {
3748  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3749  }
3750  }
3751  // Send content-length header.
3752  // Notice that all HTML content outside the length of the content-length header will be cut off!
3753  // Therefore content of unknown length from included PHP-scripts and if admin users are logged
3754  // in (admin panel might show...) or if debug mode is turned on, we disable it!
3755  if (
3756  (!isset($this->config['config']['enableContentLengthHeader']) || $this->config['config']['enableContentLengthHeader'])
3757  && !$this->beUserLogin && !$this->TYPO3_CONF_VARS['FE']['debug']
3758  && !$this->config['config']['debug'] && !$this->doWorkspacePreview()
3759  ) {
3760  header('Content-Length: ' . strlen($this->content));
3761  }
3762  }
3763 
3771  public function sendCacheHeaders()
3772  {
3773  // Getting status whether we can send cache control headers for proxy caching:
3774  $doCache = $this->isStaticCacheble();
3775  // 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...
3776  $loginsDeniedCfg = empty($this->config['config']['sendCacheHeaders_onlyWhenLoginDeniedInBranch']) || empty($this->loginAllowedInBranch);
3777  // Finally, when backend users are logged in, do not send cache headers at all (Admin Panel might be displayed for instance).
3778  if ($doCache && !$this->beUserLogin && !$this->doWorkspacePreview() && $loginsDeniedCfg) {
3779  // Build headers:
3780  $headers = [
3781  'Expires: ' . gmdate('D, d M Y H:i:s T', $this->cacheExpires),
3782  'ETag: "' . md5($this->content) . '"',
3783  'Cache-Control: max-age=' . ($this->cacheExpires - $GLOBALS['EXEC_TIME']),
3784  // no-cache
3785  'Pragma: public'
3786  ];
3787  $this->isClientCachable = true;
3788  } else {
3789  // Build headers
3790  // "no-store" is used to ensure that the client HAS to ask the server every time, and is not allowed to store anything at all
3791  $headers = [
3792  'Cache-Control: private, no-store'
3793  ];
3794  $this->isClientCachable = false;
3795  // Now, if a backend user is logged in, tell him in the Admin Panel log what the caching status would have been:
3796  if ($this->beUserLogin) {
3797  if ($doCache) {
3798  $this->getTimeTracker()->setTSlogMessage('Cache-headers with max-age "' . ($this->cacheExpires - $GLOBALS['EXEC_TIME']) . '" would have been sent');
3799  } else {
3800  $reasonMsg = '';
3801  $reasonMsg .= !$this->no_cache ? '' : 'Caching disabled (no_cache). ';
3802  $reasonMsg .= !$this->isINTincScript() ? '' : '*_INT object(s) on page. ';
3803  $reasonMsg .= !is_array($this->fe_user->user) ? '' : 'Frontend user logged in. ';
3804  $this->getTimeTracker()->setTSlogMessage('Cache-headers would disable proxy caching! Reason(s): "' . $reasonMsg . '"', 1);
3805  }
3806  }
3807  }
3808  // Send headers:
3809  foreach ($headers as $hL) {
3810  header($hL);
3811  }
3812  }
3813 
3824  public function isStaticCacheble()
3825  {
3826  $doCache = !$this->no_cache && !$this->isINTincScript() && !$this->isUserOrGroupSet();
3827  return $doCache;
3828  }
3829 
3835  public function contentStrReplace()
3836  {
3837  $search = [];
3838  $replace = [];
3839  // Substitutes username mark with the username
3840  if (!empty($this->fe_user->user['uid'])) {
3841  // User name:
3842  $token = isset($this->config['config']['USERNAME_substToken']) ? trim($this->config['config']['USERNAME_substToken']) : '';
3843  $search[] = $token ? $token : '<!--###USERNAME###-->';
3844  $replace[] = htmlspecialchars($this->fe_user->user['username']);
3845  // User uid (if configured):
3846  $token = isset($this->config['config']['USERUID_substToken']) ? trim($this->config['config']['USERUID_substToken']) : '';
3847  if ($token) {
3848  $search[] = $token;
3849  $replace[] = $this->fe_user->user['uid'];
3850  }
3851  }
3852  // Substitutes get_URL_ID in case of GET-fallback
3853  if ($this->getMethodUrlIdToken) {
3854  $search[] = $this->getMethodUrlIdToken;
3855  $replace[] = $this->fe_user->get_URL_ID;
3856  }
3857  // Hook for supplying custom search/replace data
3858  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-contentStrReplace'])) {
3859  $contentStrReplaceHooks = &$this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['tslib_fe-contentStrReplace'];
3860  if (is_array($contentStrReplaceHooks)) {
3861  $_params = [
3862  'search' => &$search,
3863  'replace' => &$replace
3864  ];
3865  foreach ($contentStrReplaceHooks as $_funcRef) {
3866  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3867  }
3868  }
3869  }
3870  if (!empty($search)) {
3871  $this->content = str_replace($search, $replace, $this->content);
3872  }
3873  }
3874 
3880  public function storeSessionData()
3881  {
3882  $this->fe_user->storeSessionData();
3883  }
3884 
3891  public function setParseTime()
3892  {
3893  // Compensates for the time consumed with Back end user initialization.
3894  $microtime_start = isset($GLOBALS['TYPO3_MISC']['microtime_start']) ? $GLOBALS['TYPO3_MISC']['microtime_start'] : null;
3895  $microtime_end = isset($GLOBALS['TYPO3_MISC']['microtime_end']) ? $GLOBALS['TYPO3_MISC']['microtime_end'] : null;
3896  $microtime_BE_USER_start = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_start']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_start'] : null;
3897  $microtime_BE_USER_end = isset($GLOBALS['TYPO3_MISC']['microtime_BE_USER_end']) ? $GLOBALS['TYPO3_MISC']['microtime_BE_USER_end'] : null;
3898  $timeTracker = $this->getTimeTracker();
3899  $this->scriptParseTime = $timeTracker->getMilliseconds($microtime_end) - $timeTracker->getMilliseconds($microtime_start) - ($timeTracker->getMilliseconds($microtime_BE_USER_end) - $timeTracker->getMilliseconds($microtime_BE_USER_start));
3900  }
3901 
3907  public function previewInfo()
3908  {
3909  if ($this->fePreview !== 0) {
3910  $previewInfo = '';
3911  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo'])) {
3912  $_params = ['pObj' => &$this];
3913  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_previewInfo'] as $_funcRef) {
3914  $previewInfo .= GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3915  }
3916  }
3917  $this->content = str_ireplace('</body>', $previewInfo . '</body>', $this->content);
3918  }
3919  }
3920 
3926  public function hook_eofe()
3927  {
3928  // Call hook for end-of-frontend processing:
3929  if (isset($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe']) && is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe'])) {
3930  $_params = ['pObj' => &$this];
3931  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['hook_eofe'] as $_funcRef) {
3932  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
3933  }
3934  }
3935  }
3936 
3942  public function beLoginLinkIPList()
3943  {
3944  if (!empty($this->config['config']['beLoginLinkIPList'])) {
3945  if (GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $this->config['config']['beLoginLinkIPList'])) {
3946  $label = !$this->beUserLogin ? $this->config['config']['beLoginLinkIPList_login'] : $this->config['config']['beLoginLinkIPList_logout'];
3947  if ($label) {
3948  if (!$this->beUserLogin) {
3949  $link = '<a href="' . htmlspecialchars((TYPO3_mainDir . 'index.php?redirect_url=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')))) . '">' . $label . '</a>';
3950  } else {
3951  $link = '<a href="' . htmlspecialchars((TYPO3_mainDir . 'index.php?L=OUT&redirect_url=' . rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI')))) . '">' . $label . '</a>';
3952  }
3953  return $link;
3954  }
3955  }
3956  }
3957  return '';
3958  }
3959 
3965  public function addTempContentHttpHeaders()
3966  {
3967  header('HTTP/1.0 503 Service unavailable');
3968  header('Retry-after: 3600');
3969  header('Pragma: no-cache');
3970  header('Cache-control: no-cache');
3971  header('Expire: 0');
3972  }
3973 
3974  /********************************************
3975  *
3976  * Various internal API functions
3977  *
3978  *******************************************/
3989  public function encryptCharcode($n, $start, $end, $offset)
3990  {
3991  $n = $n + $offset;
3992  if ($offset > 0 && $n > $end) {
3993  $n = $start + ($n - $end - 1);
3994  } elseif ($offset < 0 && $n < $start) {
3995  $n = $end - ($start - $n - 1);
3996  }
3997  return chr($n);
3998  }
3999 
4007  public function encryptEmail($string, $back = false)
4008  {
4009  $out = '';
4010  if ($this->spamProtectEmailAddresses === 'ascii') {
4011  $stringLength = strlen($string);
4012  for ($a = 0; $a < $stringLength; $a++) {
4013  $out .= '&#' . ord(substr($string, $a, 1)) . ';';
4014  }
4015  } else {
4016  // like str_rot13() but with a variable offset and a wider character range
4017  $len = strlen($string);
4018  $offset = (int)$this->spamProtectEmailAddresses * ($back ? -1 : 1);
4019  for ($i = 0; $i < $len; $i++) {
4020  $charValue = ord($string[$i]);
4021  // 0-9 . , - + / :
4022  if ($charValue >= 43 && $charValue <= 58) {
4023  $out .= $this->encryptCharcode($charValue, 43, 58, $offset);
4024  } elseif ($charValue >= 64 && $charValue <= 90) {
4025  // A-Z @
4026  $out .= $this->encryptCharcode($charValue, 64, 90, $offset);
4027  } elseif ($charValue >= 97 && $charValue <= 122) {
4028  // a-z
4029  $out .= $this->encryptCharcode($charValue, 97, 122, $offset);
4030  } else {
4031  $out .= $string[$i];
4032  }
4033  }
4034  }
4035  return $out;
4036  }
4037 
4045  public function checkFileInclude($incFile)
4046  {
4048  return !$this->TYPO3_CONF_VARS['FE']['noPHPscriptInclude'] || substr($incFile, 0, 4 + strlen(TYPO3_mainDir)) == TYPO3_mainDir . 'ext/' || substr($incFile, 0, 7 + strlen(TYPO3_mainDir)) == TYPO3_mainDir . 'sysext/' || substr($incFile, 0, 14) == 'typo3conf/ext/';
4049  }
4050 
4058  public function newCObj()
4059  {
4060  $this->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
4061  $this->cObj->start($this->page, 'pages');
4062  }
4063 
4071  public function setAbsRefPrefix()
4072  {
4073  if (!$this->absRefPrefix) {
4074  return;
4075  }
4076  $search = [
4077  '"typo3temp/',
4078  '"typo3conf/ext/',
4079  '"' . TYPO3_mainDir . 'ext/',
4080  '"' . TYPO3_mainDir . 'sysext/'
4081  ];
4082  $replace = [
4083  '"' . $this->absRefPrefix . 'typo3temp/',
4084  '"' . $this->absRefPrefix . 'typo3conf/ext/',
4085  '"' . $this->absRefPrefix . TYPO3_mainDir . 'ext/',
4086  '"' . $this->absRefPrefix . TYPO3_mainDir . 'sysext/'
4087  ];
4089  $storageRepository = GeneralUtility::makeInstance(StorageRepository::class);
4090  $storages = $storageRepository->findAll();
4091  foreach ($storages as $storage) {
4092  if ($storage->getDriverType() === 'Local' && $storage->isPublic() && $storage->isOnline()) {
4093  $folder = $storage->getPublicUrl($storage->getRootLevelFolder(), true);
4094  $search[] = '"' . $folder;
4095  $replace[] = '"' . $this->absRefPrefix . $folder;
4096  }
4097  }
4098  // Process additional directories
4099  $directories = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['additionalAbsRefPrefixDirectories'], true);
4100  foreach ($directories as $directory) {
4101  $search[] = '"' . $directory;
4102  $replace[] = '"' . $this->absRefPrefix . $directory;
4103  }
4104  $this->content = str_replace(
4105  $search,
4106  $replace,
4107  $this->content
4108  );
4109  }
4110 
4118  public function baseUrlWrap($url)
4119  {
4120  if ($this->baseUrl) {
4121  $urlParts = parse_url($url);
4122  if (empty($urlParts['scheme']) && $url[0] !== '/') {
4123  $url = $this->baseUrl . $url;
4124  }
4125  }
4126  return $url;
4127  }
4128 
4138  public function logDeprecatedTyposcript($typoScriptProperty, $explanation = '')
4139  {
4140  $explanationText = $explanation !== '' ? ' - ' . $explanation : '';
4141  $this->getTimeTracker()->setTSlogMessage($typoScriptProperty . ' is deprecated.' . $explanationText, 2);
4142  GeneralUtility::deprecationLog('TypoScript ' . $typoScriptProperty . ' is deprecated' . $explanationText);
4143  }
4144 
4152  public function updateMD5paramsRecord($hash)
4153  {
4154  $this->getDatabaseConnection()->exec_UPDATEquery('cache_md5params', 'md5hash=' . $this->getDatabaseConnection()->fullQuoteStr($hash, 'cache_md5params'), ['tstamp' => $GLOBALS['EXEC_TIME']]);
4155  }
4156 
4164  {
4166  if (!$this->beUserLogin) {
4167  if (!is_object($this->cObj)) {
4168  $this->newCObj();
4169  }
4170  $scriptPath = $this->cObj->getUrlToCurrentLocation();
4171  } else {
4172  // To break less existing sites, we allow the REQUEST_URI to be used for the prefix
4173  $scriptPath = GeneralUtility::getIndpEnv('REQUEST_URI');
4174  // Disable the cache so that these URI will not be the ones to be cached
4175  $this->disableCache();
4176  }
4177  $originalContent = $this->content;
4178  $this->content = preg_replace('/(<(?:a|area).*?href=")(#[^"]*")/i', '${1}' . htmlspecialchars($scriptPath) . '${2}', $originalContent);
4179  // There was an error in the call to preg_replace, so keep the original content (behavior prior to PHP 5.2)
4180  if (preg_last_error() > 0) {
4181  GeneralUtility::sysLog('preg_replace returned error-code: ' . preg_last_error() . ' in function prefixLocalAnchorsWithScript. Replacement not done!', 'cms', GeneralUtility::SYSLOG_SEVERITY_FATAL);
4182  $this->content = $originalContent;
4183  }
4184  }
4185 
4186  /********************************************
4187  * PUBLIC ACCESSIBLE WORKSPACES FUNCTIONS
4188  *******************************************/
4189 
4195  public function doWorkspacePreview()
4196  {
4197  return $this->workspacePreview !== 0;
4198  }
4199 
4206  public function whichWorkspace($returnTitle = false)
4207  {
4208  $ws = null;
4209  if ($this->doWorkspacePreview()) {
4210  $ws = (int)$this->workspacePreview;
4211  } elseif ($this->beUserLogin) {
4212  $ws = $this->getBackendUser()->workspace;
4213  }
4214  if ($ws && $returnTitle) {
4215  if (ExtensionManagementUtility::isLoaded('workspaces')) {
4216  $row = $this->getDatabaseConnection()->exec_SELECTgetSingleRow('title', 'sys_workspace', 'uid=' . (int)$ws);
4217  if ($row) {
4218  return $row['title'];
4219  }
4220  }
4221  }
4222  return $ws;
4223  }
4224 
4232  public function includeLibraries(array $libraries)
4233  {
4234  $timeTracker = $this->getTimeTracker();
4235  $timeTracker->push('Include libraries');
4236  $timeTracker->setTSlogMessage('Files for inclusion: "' . implode(', ', $libraries) . '"');
4237  foreach ($libraries as $library) {
4238  $file = $this->tmpl->getFileName($library);
4239  if ($file) {
4240  include_once './' . $file;
4241  } else {
4242  $timeTracker->setTSlogMessage('Include file "' . $file . '" did not exist!', 2);
4243  }
4244  }
4245  $timeTracker->pull();
4246  }
4247 
4248  /********************************************
4249  *
4250  * Various external API functions - for use in plugins etc.
4251  *
4252  *******************************************/
4259  public function getStorageSiterootPids()
4260  {
4262  $res = [];
4263  if (!is_array($this->rootLine)) {
4264  return [];
4265  }
4266  foreach ($this->rootLine as $rC) {
4267  if (!$res['_STORAGE_PID']) {
4268  $res['_STORAGE_PID'] = (int)$rC['storage_pid'];
4269  }
4270  if (!$res['_SITEROOT']) {
4271  $res['_SITEROOT'] = $rC['is_siteroot'] ? (int)$rC['uid'] : 0;
4272  }
4273  }
4274  return $res;
4275  }
4276 
4282  public function getPagesTSconfig()
4283  {
4284  if (!is_array($this->pagesTSconfig)) {
4285  $TSdataArray = [];
4286  foreach ($this->rootLine as $k => $v) {
4287  $TSdataArray[] = $v['TSconfig'];
4288  }
4289  // Adding the default configuration:
4290  $TSdataArray[] = $this->TYPO3_CONF_VARS['BE']['defaultPageTSconfig'];
4291  // Bring everything in the right order. Default first, then the Rootline down to the current page
4292  $TSdataArray = array_reverse($TSdataArray);
4293  // Parsing the user TS (or getting from cache)
4294  $TSdataArray = TypoScriptParser::checkIncludeLines_array($TSdataArray);
4295  $userTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
4296  $hash = md5('pageTS:' . $userTS);
4297  $cachedContent = $this->sys_page->getHash($hash);
4298  if (is_array($cachedContent)) {
4299  $this->pagesTSconfig = $cachedContent;
4300  } else {
4301  $parseObj = GeneralUtility::makeInstance(TypoScriptParser::class);
4302  $parseObj->parse($userTS);
4303  $this->pagesTSconfig = $parseObj->setup;
4304  $this->sys_page->storeHash($hash, $this->pagesTSconfig, 'PAGES_TSconfig');
4305  }
4306  }
4307  return $this->pagesTSconfig;
4308  }
4309 
4318  public function setJS($key, $content = '')
4319  {
4320  if ($key) {
4321  switch ($key) {
4322  case 'mouseOver':
4323  $this->additionalJavaScript[$key] = ' // JS function for mouse-over
4324  function over(name, imgObj) { //
4325  if (document[name]) {document[name].src = eval(name+"_h.src");}
4326  else if (document.getElementById && document.getElementById(name)) {document.getElementById(name).src = eval(name+"_h.src");}
4327  else if (imgObj) {imgObj.src = eval(name+"_h.src");}
4328  }
4329  // JS function for mouse-out
4330  function out(name, imgObj) { //
4331  if (document[name]) {document[name].src = eval(name+"_n.src");}
4332  else if (document.getElementById && document.getElementById(name)) {document.getElementById(name).src = eval(name+"_n.src");}
4333  else if (imgObj) {imgObj.src = eval(name+"_n.src");}
4334  }';
4335  break;
4336  case 'openPic':
4337  $this->additionalJavaScript[$key] = ' function openPic(url, winName, winParams) { //
4338  var theWindow = window.open(url, winName, winParams);
4339  if (theWindow) {theWindow.focus();}
4340  }';
4341  break;
4342  default:
4343  $this->additionalJavaScript[$key] = $content;
4344  }
4345  }
4346  }
4347 
4356  public function setCSS($key, $content)
4357  {
4358  if ($key) {
4359  $this->additionalCSS[$key] = $content;
4360  }
4361  }
4362 
4370  public function uniqueHash($str = '')
4371  {
4372  return md5($this->uniqueString . '_' . $str . $this->uniqueCounter++);
4373  }
4374 
4382  public function set_no_cache($reason = '', $internal = false)
4383  {
4384  if ($internal && isset($GLOBALS['BE_USER'])) {
4386  } else {
4388  }
4389 
4390  if ($reason !== '') {
4391  $warning = '$TSFE->set_no_cache() was triggered. Reason: ' . $reason . '.';
4392  } else {
4393  $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
4394  // This is a hack to work around ___FILE___ resolving symbolic links
4395  $PATH_site_real = dirname(realpath(PATH_site . 'typo3')) . '/';
4396  $file = $trace[0]['file'];
4397  if (StringUtility::beginsWith($file, $PATH_site_real)) {
4398  $file = str_replace($PATH_site_real, '', $file);
4399  } else {
4400  $file = str_replace(PATH_site, '', $file);
4401  }
4402  $line = $trace[0]['line'];
4403  $trigger = $file . ' on line ' . $line;
4404  $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by ' . $trigger . '.';
4405  }
4406  if ($this->TYPO3_CONF_VARS['FE']['disableNoCacheParameter']) {
4407  $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!';
4408  $this->getTimeTracker()->setTSlogMessage($warning, 2);
4409  } else {
4410  $warning .= ' Caching is disabled!';
4411  $this->disableCache();
4412  }
4413  GeneralUtility::sysLog($warning, 'cms', $severity);
4414  }
4415 
4422  protected function disableCache()
4423  {
4424  $this->no_cache = true;
4425  }
4426 
4433  public function set_cache_timeout_default($seconds)
4434  {
4435  $this->cacheTimeOutDefault = (int)$seconds;
4436  }
4437 
4443  public function get_cache_timeout()
4444  {
4446  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4447  $cachedCacheLifetimeIdentifier = 'core-tslib_fe-get_cache_timeout';
4448  $cachedCacheLifetime = $runtimeCache->get($cachedCacheLifetimeIdentifier);
4449  if ($cachedCacheLifetime === false) {
4450  if ($this->page['cache_timeout']) {
4451  // Cache period was set for the page:
4452  $cacheTimeout = $this->page['cache_timeout'];
4453  } elseif ($this->cacheTimeOutDefault) {
4454  // Cache period was set for the whole site:
4455  $cacheTimeout = $this->cacheTimeOutDefault;
4456  } else {
4457  // No cache period set at all, so we take one day (60*60*24 seconds = 86400 seconds):
4458  $cacheTimeout = 86400;
4459  }
4460  if ($this->config['config']['cache_clearAtMidnight']) {
4461  $timeOutTime = $GLOBALS['EXEC_TIME'] + $cacheTimeout;
4462  $midnightTime = mktime(0, 0, 0, date('m', $timeOutTime), date('d', $timeOutTime), date('Y', $timeOutTime));
4463  // If the midnight time of the expire-day is greater than the current time,
4464  // we may set the timeOutTime to the new midnighttime.
4465  if ($midnightTime > $GLOBALS['EXEC_TIME']) {
4466  $cacheTimeout = $midnightTime - $GLOBALS['EXEC_TIME'];
4467  }
4468  }
4469 
4470  // Calculate the timeout time for records on the page and adjust cache timeout if necessary
4471  $cacheTimeout = min($this->calculatePageCacheTimeout(), $cacheTimeout);
4472 
4473  if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['get_cache_timeout'])) {
4474  foreach ($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['get_cache_timeout'] as $_funcRef) {
4475  $params = ['cacheTimeout' => $cacheTimeout];
4476  $cacheTimeout = GeneralUtility::callUserFunction($_funcRef, $params, $this);
4477  }
4478  }
4479  $runtimeCache->set($cachedCacheLifetimeIdentifier, $cacheTimeout);
4480  $cachedCacheLifetime = $cacheTimeout;
4481  }
4482  return $cachedCacheLifetime;
4483  }
4484 
4491  public function getUniqueId($desired = '')
4492  {
4493  if ($desired === '') {
4494  // id has to start with a letter to reach XHTML compliance
4495  $uniqueId = 'a' . $this->uniqueHash();
4496  } else {
4497  $uniqueId = $desired;
4498  for ($i = 1; isset($this->usedUniqueIds[$uniqueId]); $i++) {
4499  $uniqueId = $desired . '_' . $i;
4500  }
4501  }
4502  $this->usedUniqueIds[$uniqueId] = true;
4503  return $uniqueId;
4504  }
4505 
4506  /*********************************************
4507  *
4508  * Localization and character set conversion
4509  *
4510  *********************************************/
4517  public function sL($input)
4518  {
4519  if (substr($input, 0, 4) !== 'LLL:') {
4520  // Not a label, return the key as this
4521  return $input;
4522  }
4523  // If cached label
4524  if (!isset($this->LL_labels_cache[$this->lang][$input])) {
4525  $restStr = trim(substr($input, 4));
4526  $extPrfx = '';
4527  if (substr($restStr, 0, 4) === 'EXT:') {
4528  $restStr = trim(substr($restStr, 4));
4529  $extPrfx = 'EXT:';
4530  }
4531  $parts = explode(':', $restStr);
4532  $parts[0] = $extPrfx . $parts[0];
4533  // Getting data if not cached
4534  if (!isset($this->LL_files_cache[$parts[0]])) {
4535  $this->LL_files_cache[$parts[0]] = $this->readLLfile($parts[0]);
4536  }
4537  $this->LL_labels_cache[$this->lang][$input] = $this->getLLL($parts[1], $this->LL_files_cache[$parts[0]]);
4538  }
4539  return $this->LL_labels_cache[$this->lang][$input];
4540  }
4541 
4548  public function readLLfile($fileRef)
4549  {
4551  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
4552 
4553  if ($this->lang !== 'default') {
4554  $languages = array_reverse($this->languageDependencies);
4555  // At least we need to have English
4556  if (empty($languages)) {
4557  $languages[] = 'default';
4558  }
4559  } else {
4560  $languages = ['default'];
4561  }
4562 
4563  $localLanguage = [];
4564  foreach ($languages as $language) {
4565  $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->renderCharset);
4566  $localLanguage['default'] = $tempLL['default'];
4567  if (!isset($localLanguage[$this->lang])) {
4568  $localLanguage[$this->lang] = $localLanguage['default'];
4569  }
4570  if ($this->lang !== 'default' && isset($tempLL[$language])) {
4571  // Merge current language labels onto labels from previous language
4572  // This way we have a label with fall back applied
4573  ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
4574  }
4575  }
4576 
4577  return $localLanguage;
4578  }
4579 
4587  public function getLLL($index, $LOCAL_LANG)
4588  {
4589  if (isset($LOCAL_LANG[$this->lang][$index][0]['target'])) {
4590  return $LOCAL_LANG[$this->lang][$index][0]['target'];
4591  } elseif (isset($LOCAL_LANG['default'][$index][0]['target'])) {
4592  return $LOCAL_LANG['default'][$index][0]['target'];
4593  }
4594  return false;
4595  }
4596 
4602  public function initLLvars()
4603  {
4604  // Init languageDependencies list
4605  $this->languageDependencies = [];
4606  // Setting language key and split index:
4607  $this->lang = $this->config['config']['language'] ?: 'default';
4608  $this->pageRenderer->setLanguage($this->lang);
4609 
4610  // Finding the requested language in this list based
4611  // on the $lang key being inputted to this function.
4613  $locales = GeneralUtility::makeInstance(Locales::class);
4614  $locales->initialize();
4615 
4616  // Language is found. Configure it:
4617  if (in_array($this->lang, $locales->getLocales())) {
4618  $this->languageDependencies[] = $this->lang;
4619  foreach ($locales->getLocaleDependencies($this->lang) as $language) {
4620  $this->languageDependencies[] = $language;
4621  }
4622  }
4623 
4624  // Setting charsets:
4625  $this->renderCharset = $this->csConvObj->parse_charset($this->config['config']['renderCharset'] ? $this->config['config']['renderCharset'] : 'utf-8');
4626  // Rendering charset of HTML page.
4627  $this->metaCharset = $this->csConvObj->parse_charset($this->config['config']['metaCharset'] ? $this->config['config']['metaCharset'] : $this->renderCharset);
4628  }
4629 
4641  public function csConv($str, $from = '')
4642  {
4643  if ($from) {
4644  $output = $this->csConvObj->conv($str, $this->csConvObj->parse_charset($from), $this->renderCharset, 1);
4645  return $output ?: $str;
4646  } else {
4647  return $str;
4648  }
4649  }
4650 
4657  public function convOutputCharset($content)
4658  {
4659  if ($this->renderCharset != $this->metaCharset) {
4660  $content = $this->csConvObj->conv($content, $this->renderCharset, $this->metaCharset, true);
4661  }
4662  return $content;
4663  }
4664 
4670  public function convPOSTCharset()
4671  {
4672  if ($this->renderCharset != $this->metaCharset && is_array($_POST) && !empty($_POST)) {
4673  $this->csConvObj->convArray($_POST, $this->metaCharset, $this->renderCharset);
4674  $GLOBALS['HTTP_POST_VARS'] = $_POST;
4675  }
4676  }
4677 
4683  protected function calculatePageCacheTimeout()
4684  {
4685  $result = PHP_INT_MAX;
4686  // Get the configuration
4687  $tablesToConsider = $this->getCurrentPageCacheConfiguration();
4688  // Get the time, rounded to the minute (do not polute MySQL cache!)
4689  // It is ok that we do not take seconds into account here because this
4690  // value will be substracted later. So we never get the time "before"
4691  // the cache change.
4692  $now = $GLOBALS['ACCESS_TIME'];
4693  // Find timeout by checking every table
4694  foreach ($tablesToConsider as $tableDef) {
4695  $result = min($result, $this->getFirstTimeValueForRecord($tableDef, $now));
4696  }
4697  // We return + 1 second just to ensure that cache is definitely regenerated
4698  return $result == PHP_INT_MAX ? PHP_INT_MAX : $result - $now + 1;
4699  }
4700 
4716  {
4717  $result = ['tt_content:' . $this->id];
4718  if (isset($this->config['config']['cache.'][$this->id])) {
4719  $result = array_merge($result, GeneralUtility::trimExplode(',', $this->config['config']['cache.'][$this->id]));
4720  }
4721  if (isset($this->config['config']['cache.']['all'])) {
4722  $result = array_merge($result, GeneralUtility::trimExplode(',', $this->config['config']['cache.']['all']));
4723  }
4724  return array_unique($result);
4725  }
4726 
4736  protected function getFirstTimeValueForRecord($tableDef, $now)
4737  {
4738  $result = PHP_INT_MAX;
4739  list($tableName, $pid) = GeneralUtility::trimExplode(':', $tableDef);
4740  if (empty($tableName) || empty($pid)) {
4741  throw new \InvalidArgumentException('Unexpected value for parameter $tableDef. Expected <tablename>:<pid>, got \'' . htmlspecialchars($tableDef) . '\'.', 1307190365);
4742  }
4743  // Additional fields
4744  $showHidden = $tableName === 'pages' ? $this->showHiddenPage : $this->showHiddenRecords;
4745  $enableFields = $this->sys_page->enableFields(
4746  $tableName,
4747  $showHidden,
4748  ['starttime' => true, 'endtime' => true]
4749  );
4750 
4751  $timeFields = [];
4752  $selectFields = [];
4753  $whereConditions = [];
4754  foreach (['starttime', 'endtime'] as $field) {
4755  if (isset($GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field])) {
4756  $timeFields[$field] = $GLOBALS['TCA'][$tableName]['ctrl']['enablecolumns'][$field];
4757  $selectFields[$field]
4758  = 'MIN('
4759  . 'CASE WHEN ' . $timeFields[$field] . ' <= ' . $now
4760  . ' THEN NULL ELSE ' . $timeFields[$field] . ' END'
4761  . ') AS ' . $field;
4762  $whereConditions[$field] = $timeFields[$field] . '>' . $now;
4763  }
4764  }
4765 
4766  // if starttime or endtime are defined, evaluate them
4767  if (!empty($timeFields)) {
4768  // find the timestamp, when the current page's content changes the next time
4769  $row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
4770  implode(', ', $selectFields),
4771  $tableName,
4772  'pid=' . (int)$pid
4773  . ' AND (' . implode(' OR ', $whereConditions) . ')'
4774  . $enableFields
4775  );
4776  if ($row) {
4777  foreach ($timeFields as $timeField => $_) {
4778  // if a MIN value is found, take it into account for the
4779  // cache lifetime we have to filter out start/endtimes < $now,
4780  // as the SQL query also returns rows with starttime < $now
4781  // and endtime > $now (and using a starttime from the past
4782  // would be wrong)
4783  if ($row[$timeField] !== null && (int)$row[$timeField] > $now) {
4784  $result = min($result, (int)$row[$timeField]);
4785  }
4786  }
4787  }
4788  }
4789 
4790  return $result;
4791  }
4792 
4798  protected function getSysDomainCache()
4799  {
4800  $entryIdentifier = 'core-database-sys_domain-complete';
4802  $runtimeCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_runtime');
4803 
4804  $sysDomainData = [];
4805  if ($runtimeCache->has($entryIdentifier)) {
4806  $sysDomainData = $runtimeCache->get($entryIdentifier);
4807  } else {
4808  $domainRecords = $this->getDatabaseConnection()->exec_SELECTgetRows(
4809  'uid, pid, domainName, forced',
4810  'sys_domain',
4811  'redirectTo=\'\' ' . $this->sys_page->enableFields('sys_domain', 0),
4812  '',
4813  'sorting ASC'
4814  );
4815 
4816  foreach ($domainRecords as $row) {
4817  // if there is already an entry for this pid, check if we should overwrite it
4818  if (isset($sysDomainData[$row['pid']])) {
4819  // There is already a "forced" entry, which must not be overwritten
4820  if ($sysDomainData[$row['pid']]['forced']) {
4821  continue;
4822  }
4823 
4824  // The current domain record is also NOT-forced, keep the old unless the new one matches the current request
4825  if (!$row['forced'] && !$this->domainNameMatchesCurrentRequest($row['domainName'])) {
4826  continue;
4827  }
4828  }
4829 
4830  // as we passed all previous checks, we save this domain for the current pid
4831  $sysDomainData[$row['pid']] = [
4832  'uid' => $row['uid'],
4833  'pid' => $row['pid'],
4834  'domainName' => rtrim($row['domainName'], '/'),
4835  'forced' => $row['forced'],
4836  ];
4837  }
4838  $runtimeCache->set($entryIdentifier, $sysDomainData);
4839  }
4840  return $sysDomainData;
4841  }
4842 
4850  public function domainNameMatchesCurrentRequest($domainName)
4851  {
4852  $currentDomain = GeneralUtility::getIndpEnv('HTTP_HOST');
4853  $currentPathSegment = trim(preg_replace('|/[^/]*$|', '', GeneralUtility::getIndpEnv('SCRIPT_NAME')));
4854  return $currentDomain === $domainName || $currentDomain . $currentPathSegment === $domainName;
4855  }
4856 
4865  public function getDomainDataForPid($targetPid)
4866  {
4867  // Using array_key_exists() here, nice $result can be NULL
4868  // (happens, if there's no domain records defined)
4869  if (!array_key_exists($targetPid, $this->domainDataCache)) {
4870  $result = null;
4871  $sysDomainData = $this->getSysDomainCache();
4872  $rootline = $this->sys_page->getRootLine($targetPid);
4873  // walk the rootline downwards from the target page
4874  // to the root page, until a domain record is found
4875  foreach ($rootline as $pageInRootline) {
4876  $pidInRootline = $pageInRootline['uid'];
4877  if (isset($sysDomainData[$pidInRootline])) {
4878  $result = $sysDomainData[$pidInRootline];
4879  break;
4880  }
4881  }
4882  $this->domainDataCache[$targetPid] = $result;
4883  }
4884 
4885  return $this->domainDataCache[$targetPid];
4886  }
4887 
4895  public function getDomainNameForPid($targetPid)
4896  {
4897  $domainData = $this->getDomainDataForPid($targetPid);
4898  return $domainData ? $domainData['domainName'] : null;
4899  }
4900 
4907  public function getRequestedId()
4908  {
4909  return $this->requestedId ?: $this->id;
4910  }
4911 
4921  protected function acquireLock($type, $key)
4922  {
4923  $lockFactory = GeneralUtility::makeInstance(LockFactory::class);
4924  $this->locks[$type]['accessLock'] = $lockFactory->createLocker($type);
4925 
4926  $this->locks[$type]['pageLock'] = $lockFactory->createLocker(
4927  $key,
4929  );
4930 
4931  do {
4932  if (!$this->locks[$type]['accessLock']->acquire()) {
4933  throw new \RuntimeException('Could not acquire access lock for "' . $type . '"".', 1294586098);
4934  }
4935 
4936  try {
4937  $locked = $this->locks[$type]['pageLock']->acquire(
4939  );
4940  } catch (LockAcquireWouldBlockException $e) {
4941  // somebody else has the lock, we keep waiting
4942 
4943  // first release the access lock
4944  $this->locks[$type]['accessLock']->release();
4945  // now lets make a short break (100ms) until we try again, since
4946  // the page generation by the lock owner will take a while anyways
4947  usleep(100000);
4948  continue;
4949  }
4950  $this->locks[$type]['accessLock']->release();
4951  if ($locked) {
4952  break;
4953  } else {
4954  throw new \RuntimeException('Could not acquire page lock for ' . $key . '.', 1294586098);
4955  }
4956  } while (true);
4957  }
4958 
4967  protected function releaseLock($type)
4968  {
4969  if ($this->locks[$type]['accessLock']) {
4970  if (!$this->locks[$type]['accessLock']->acquire()) {
4971  throw new \RuntimeException('Could not acquire access lock for "' . $type . '"".', 1294586098);
4972  }
4973 
4974  $this->locks[$type]['pageLock']->release();
4975  $this->locks[$type]['pageLock']->destroy();
4976  $this->locks[$type]['pageLock'] = null;
4977 
4978  $this->locks[$type]['accessLock']->release();
4979  $this->locks[$type]['accessLock'] = null;
4980  }
4981  }
4982 
4988  protected function getBackendUser()
4989  {
4990  return $GLOBALS['BE_USER'];
4991  }
4992 
4998  protected function getDatabaseConnection()
4999  {
5000  return $GLOBALS['TYPO3_DB'];
5001  }
5002 
5006  protected function getTimeTracker()
5007  {
5008  return $GLOBALS['TT'];
5009  }
5010 
5016  protected function getDocumentTemplate()
5017  {
5018  return $GLOBALS['TBE_TEMPLATE'];
5019  }
5020 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static isAllowedLinkVarValue($haystack, $needle)
static isFirstPartOfStr($str, $partStr)
static setSingletonInstance($className, SingletonInterface $instance)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static hideIfDefaultLanguage($localizationConfiguration)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static hideIfNotTranslated($l18n_cfg_fieldValue)
static _GETset($inputGet, $key='')
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static getUrl($url, $includeHeader=0, $requestHeaders=false, &$report=null)
getPageShortcut($SC, $mode, $thisUid, $itera=20, $pageLog=[], $disableGroupCheck=false)
static getFileAbsFileName($filename, $onlyRelative=true, $relToTYPO3_mainDir=false)
$host
Definition: server.php:37
static beginsWith($haystack, $needle)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
__construct($TYPO3_CONF_VARS, $id, $type, $no_cache='', $cHash='', $jumpurl='', $MP='', $RDCT='')
$locales
Definition: be_users.php:6