TYPO3  7.6
TemplateService.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\TypoScript;
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 
30 
37 {
45  protected $verbose = false;
46 
52  public $tt_track = true;
53 
59  public $forceTemplateParsing = false;
60 
68  public $matchAlternative = array();
69 
75  public $matchAll = false;
76 
80  public $backend_info = false;
81 
87  public $ext_constants_BRP = 0;
88 
92  public $ext_config_BRP = 0;
93 
97  public $ext_regLinenumbers = false;
98 
102  public $ext_regComments = false;
103 
108  public $tempPath = 'typo3temp/';
109 
115  public $whereClause = '';
116 
120  public $debug = false;
121 
128  public $allowedPaths = array();
129 
136 
142  public $loaded = false;
143 
147  public $setup = array();
148 
152  public $flatSetup = array();
153 
161  public $config = array();
162 
168  public $constants = array();
169 
175  protected $templateIncludePaths = array();
176 
182  public $hierarchyInfo = array();
183 
189  public $hierarchyInfoToRoot = array();
190 
196  public $nextLevel = 0;
197 
203  public $rootId;
204 
210  public $rootLine;
211 
218 
225 
231  public $rowSum;
232 
238  public $sitetitle = '';
239 
245  public $sections;
246 
253 
258  public $clearList_const = array();
259 
265  public $clearList_setup = array();
266 
270  public $parserErrors = array();
271 
275  public $setup_constants = array();
276 
282  public $fileCache = array();
283 
289  public $frames = array();
290 
296  public $MPmap = '';
297 
307  protected $extensionStaticsProcessed = false;
308 
314  protected $processExtensionStatics = false;
315 
323  protected $isDefaultTypoScriptAdded = false;
324 
333  protected $processIncludesHasBeenRun = false;
334 
338  public function getProcessExtensionStatics()
339  {
341  }
342 
347  {
348  $this->processExtensionStatics = (bool)$processExtensionStatics;
349  }
350 
355  public function setVerbose($verbose)
356  {
357  $this->verbose = (bool)$verbose;
358  }
359 
367  public function init()
368  {
369  // $this->whereClause is used only to select templates from sys_template.
370  // $GLOBALS['SIM_ACCESS_TIME'] is used so that we're able to simulate a later time as a test...
371  $this->whereClause = 'AND deleted=0 ';
372  if (!$this->getTypoScriptFrontendController()->showHiddenRecords) {
373  $this->whereClause .= 'AND hidden=0 ';
374  }
375  if ($this->getTypoScriptFrontendController()->showHiddenRecords || $GLOBALS['SIM_ACCESS_TIME'] != $GLOBALS['ACCESS_TIME']) {
376  // Set the simulation flag, if simulation is detected!
377  $this->simulationHiddenOrTime = 1;
378  }
379  $this->whereClause .= 'AND (starttime<=' . $GLOBALS['SIM_ACCESS_TIME'] . ') AND (endtime=0 OR endtime>' . $GLOBALS['SIM_ACCESS_TIME'] . ')';
380  // Sets the paths from where TypoScript resources are allowed to be used:
381  $this->allowedPaths = array(
382  $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'],
383  // fileadmin/ path
384  'uploads/',
385  'typo3temp/',
386  TYPO3_mainDir . 'ext/',
387  TYPO3_mainDir . 'sysext/',
388  'typo3conf/ext/'
389  );
390  if ($GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']) {
391  $pathArr = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths'], true);
392  foreach ($pathArr as $p) {
393  // Once checked for path, but as this may run from typo3/mod/web/ts/ dir, that'll not work!! So the paths ar uncritically included here.
394  $this->allowedPaths[] = $p;
395  }
396  }
397  }
398 
415  public function getCurrentPageData()
416  {
417  return GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pagesection')->get((int)$this->getTypoScriptFrontendController()->id . '_' . GeneralUtility::md5int($this->getTypoScriptFrontendController()->MP));
418  }
419 
426  public function matching($cc)
427  {
428  if (is_array($cc['all'])) {
430  $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
431  $matchObj->setRootline((array)$cc['rootLine']);
432  $sectionsMatch = array();
433  foreach ($cc['all'] as $key => $pre) {
434  if ($matchObj->match($pre)) {
435  $sectionsMatch[$key] = $pre;
436  }
437  }
438  $cc['match'] = $sectionsMatch;
439  }
440  return $cc;
441  }
442 
452  public function start($theRootLine)
453  {
454  if (is_array($theRootLine)) {
455  $setupData = '';
456  $hash = '';
457  // Flag that indicates that the existing data in cache_pagesection
458  // could be used (this is the case if $TSFE->all is set, and the
459  // rowSum still matches). Based on this we decide if cache_pagesection
460  // needs to be updated...
461  $isCached = false;
462  $this->runThroughTemplates($theRootLine);
463  if ($this->getTypoScriptFrontendController()->all) {
464  $cc = $this->getTypoScriptFrontendController()->all;
465  // The two rowSums must NOT be different from each other - which they will be if start/endtime or hidden has changed!
466  if (serialize($this->rowSum) !== serialize($cc['rowSum'])) {
467  unset($cc);
468  } else {
469  // If $TSFE->all contains valid data, we don't need to update cache_pagesection (because this data was fetched from there already)
470  if (serialize($this->rootLine) === serialize($cc['rootLine'])) {
471  $isCached = true;
472  }
473  // When the data is serialized below (ROWSUM hash), it must not contain the rootline by concept. So this must be removed (and added again later)...
474  unset($cc['rootLine']);
475  }
476  }
477  // This is about getting the hash string which is used to fetch the cached TypoScript template.
478  // If there was some cached currentPageData ($cc) then that's good (it gives us the hash).
479  if (isset($cc) && is_array($cc)) {
480  // If currentPageData was actually there, we match the result (if this wasn't done already in $TSFE->getFromCache()...)
481  if (!$cc['match']) {
482  // @todo check if this can ever be the case - otherwise remove
483  $cc = $this->matching($cc);
484  ksort($cc);
485  }
486  $hash = md5(serialize($cc));
487  } else {
488  // If currentPageData was not there, we first find $rowSum (freshly generated). After that we try to see, if it is stored with a list of all conditions. If so we match the result.
489  $rowSumHash = md5('ROWSUM:' . serialize($this->rowSum));
490  $result = PageRepository::getHash($rowSumHash);
491  if (is_array($result)) {
492  $cc = array();
493  $cc['all'] = $result;
494  $cc['rowSum'] = $this->rowSum;
495  $cc = $this->matching($cc);
496  ksort($cc);
497  $hash = md5(serialize($cc));
498  }
499  }
500  if ($hash) {
501  // Get TypoScript setup array
502  $setupData = PageRepository::getHash($hash);
503  }
504  if (is_array($setupData) && !$this->forceTemplateParsing) {
505  // If TypoScript setup structure was cached we unserialize it here:
506  $this->setup = $setupData;
507  if ($this->tt_track) {
508  $this->getTimeTracker()->setTSLogMessage('Using cached TS template data');
509  }
510  } else {
511  if ($this->tt_track) {
512  $this->getTimeTracker()->setTSLogMessage('Not using any cached TS data');
513  }
514 
515  // Make configuration
516  $this->generateConfig();
517  // This stores the template hash thing
518  $cc = array();
519  // All sections in the template at this point is found
520  $cc['all'] = $this->sections;
521  // The line of templates is collected
522  $cc['rowSum'] = $this->rowSum;
523  $cc = $this->matching($cc);
524  ksort($cc);
525  $hash = md5(serialize($cc));
526  // This stores the data.
527  PageRepository::storeHash($hash, $this->setup, 'TS_TEMPLATE');
528  if ($this->tt_track) {
529  $this->getTimeTracker()->setTSlogMessage('TS template size, serialized: ' . strlen(serialize($this->setup)) . ' bytes');
530  }
531  $rowSumHash = md5('ROWSUM:' . serialize($this->rowSum));
532  PageRepository::storeHash($rowSumHash, $cc['all'], 'TMPL_CONDITIONS_ALL');
533  }
534  // Add rootLine
535  $cc['rootLine'] = $this->rootLine;
536  ksort($cc);
537  // Make global and save
538  $this->getTypoScriptFrontendController()->all = $cc;
539  // Matching must be executed for every request, so this must never be part of the pagesection cache!
540  unset($cc['match']);
541  if (!$isCached && !$this->simulationHiddenOrTime && !$this->getTypoScriptFrontendController()->no_cache) {
542  // Only save the data if we're not simulating by hidden/starttime/endtime
543  $mpvarHash = GeneralUtility::md5int($this->getTypoScriptFrontendController()->MP);
545  $pageSectionCache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_pagesection');
546  $pageSectionCache->set((int)$this->getTypoScriptFrontendController()->id . '_' . $mpvarHash, $cc, array(
547  'pageId_' . (int)$this->getTypoScriptFrontendController()->id,
548  'mpvarHash_' . $mpvarHash
549  ));
550  }
551  // If everything OK.
552  if ($this->rootId && $this->rootLine && $this->setup) {
553  $this->loaded = true;
554  }
555  }
556  }
557 
558  /*******************************************************************
559  *
560  * Fetching TypoScript code text for the Template Hierarchy
561  *
562  *******************************************************************/
573  public function runThroughTemplates($theRootLine, $start_template_uid = 0)
574  {
575  $this->constants = array();
576  $this->config = array();
577  $this->rowSum = array();
578  $this->hierarchyInfoToRoot = array();
579  $this->absoluteRootLine = $theRootLine;
580  $this->isDefaultTypoScriptAdded = false;
581 
582  reset($this->absoluteRootLine);
583  $c = count($this->absoluteRootLine);
584  for ($a = 0; $a < $c; $a++) {
585  // If some template loaded before has set a template-id for the next level, then load this template first!
586  if ($this->nextLevel) {
587  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', 'uid=' . (int)$this->nextLevel . ' ' . $this->whereClause);
588  $this->nextLevel = 0;
589  if ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
590  $this->versionOL($row);
591  if (is_array($row)) {
592  $this->processTemplate($row, 'sys_' . $row['uid'], $this->absoluteRootLine[$a]['uid'], 'sys_' . $row['uid']);
593  $this->outermostRootlineIndexWithTemplate = $a;
594  }
595  }
596  $this->getDatabaseConnection()->sql_free_result($res);
597  }
598  $addC = '';
599  // If first loop AND there is set an alternative template uid, use that
600  if ($a == $c - 1 && $start_template_uid) {
601  $addC = ' AND uid=' . (int)$start_template_uid;
602  }
603  $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_template', 'pid=' . (int)$this->absoluteRootLine[$a]['uid'] . $addC . ' ' . $this->whereClause, '', 'root DESC, sorting', 1);
604  if ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
605  $this->versionOL($row);
606  if (is_array($row)) {
607  $this->processTemplate($row, 'sys_' . $row['uid'], $this->absoluteRootLine[$a]['uid'], 'sys_' . $row['uid']);
608  $this->outermostRootlineIndexWithTemplate = $a;
609  }
610  }
611  $this->getDatabaseConnection()->sql_free_result($res);
612  $this->rootLine[] = $this->absoluteRootLine[$a];
613  }
614 
615  // Process extension static files if not done yet, but explicitly requested
616  if (!$this->extensionStaticsProcessed && $this->processExtensionStatics) {
617  $this->addExtensionStatics('sys_0', 'sys_0', 0, array());
618  }
619 
620  // Add the global default TypoScript from the TYPO3_CONF_VARS
621  $this->addDefaultTypoScript();
622 
623  $this->processIncludes();
624  }
625 
639  public function processTemplate($row, $idList, $pid, $templateID = '', $templateParent = '', $includePath = '')
640  {
641  // Adding basic template record information to rowSum array
642  $this->rowSum[] = array($row['uid'], $row['title'], $row['tstamp']);
643  // Processing "Clear"-flags
644  $clConst = 0;
645  $clConf = 0;
646  if ($row['clear']) {
647  $clConst = $row['clear'] & 1;
648  $clConf = $row['clear'] & 2;
649  if ($clConst) {
650  // Keep amount of items to stay in sync with $this->templateIncludePaths so processIncludes() does not break
651  foreach ($this->constants as &$constantConfiguration) {
652  $constantConfiguration = '';
653  }
654  unset($constantConfiguration);
655  $this->clearList_const = array();
656  }
657  if ($clConf) {
658  // Keep amount of items to stay in sync with $this->templateIncludePaths so processIncludes() does not break
659  foreach ($this->config as &$configConfiguration) {
660  $configConfiguration = '';
661  }
662  unset($configConfiguration);
663  $this->hierarchyInfoToRoot = array();
664  $this->clearList_setup = array();
665  }
666  }
667  // Include static records (static_template) or files (from extensions) (#1/2)
668  // NORMAL inclusion, The EXACT same code is found below the basedOn inclusion!!!
669  if (!$row['includeStaticAfterBasedOn']) {
670  $this->includeStaticTypoScriptSources($idList, $templateID, $pid, $row);
671  }
672  // Include "Based On" sys_templates:
673  // 'basedOn' is a list of templates to include
674  if (trim($row['basedOn'])) {
675  // Normal Operation, which is to include the "based-on" sys_templates,
676  // if they are not already included, and maintaining the sorting of the templates
677  $basedOnIds = GeneralUtility::intExplode(',', $row['basedOn'], true);
678  // skip template if it's already included
679  foreach ($basedOnIds as $key => $basedOnId) {
680  if (GeneralUtility::inList($idList, 'sys_' . $basedOnId)) {
681  unset($basedOnIds[$key]);
682  }
683  }
684  if (!empty($basedOnIds)) {
685  $subTemplates = $this->getDatabaseConnection()->exec_SELECTgetRows('*', 'sys_template', 'uid IN (' . implode(',', $basedOnIds) . ') ' . $this->whereClause, '', '', '', 'uid');
686  // Traversing list again to ensure the sorting of the templates
687  foreach ($basedOnIds as $id) {
688  if (is_array($subTemplates[$id])) {
689  $this->versionOL($subTemplates[$id]);
690  $this->processTemplate($subTemplates[$id], $idList . ',sys_' . $id, $pid, 'sys_' . $id, $templateID);
691  }
692  }
693  }
694  }
695  // Include static records (static_template) or files (from extensions) (#2/2)
696  if ($row['includeStaticAfterBasedOn']) {
697  $this->includeStaticTypoScriptSources($idList, $templateID, $pid, $row);
698  }
699  // Creating hierarchy information; Used by backend analysis tools
700  $this->hierarchyInfo[] = ($this->hierarchyInfoToRoot[] = array(
701  'root' => trim($row['root']),
702  'next' => $row['nextLevel'],
703  'clConst' => $clConst,
704  'clConf' => $clConf,
705  'templateID' => $templateID,
706  'templateParent' => $templateParent,
707  'title' => $row['title'],
708  'uid' => $row['uid'],
709  'pid' => $row['pid'],
710  'configLines' => substr_count($row['config'], LF) + 1
711  ));
712  // Adding the content of the fields constants (Constants) and config (Setup)
713  $this->constants[] = $row['constants'];
714  $this->config[] = $row['config'];
715  $this->templateIncludePaths[] = $includePath;
716  // For backend analysis (Template Analyser) provide the order of added constants/config template IDs
717  $this->clearList_const[] = $templateID;
718  $this->clearList_setup[] = $templateID;
719  if (trim($row['sitetitle'])) {
720  $this->sitetitle = $row['sitetitle'];
721  }
722  // If the template record is a Rootlevel record, set the flag and clear the template rootLine (so it starts over from this point)
723  if (trim($row['root'])) {
724  $this->rootId = $pid;
725  $this->rootLine = array();
726  }
727  // If a template is set to be active on the next level set this internal value to point to this UID. (See runThroughTemplates())
728  if ($row['nextLevel']) {
729  $this->nextLevel = $row['nextLevel'];
730  } else {
731  $this->nextLevel = 0;
732  }
733  }
734 
746  public function updateRootlineData($fullRootLine)
747  {
748  if (!is_array($this->rootLine) || empty($this->rootLine)) {
749  return;
750  }
751 
752  $fullRootLineByUid = array();
753  foreach ($fullRootLine as $rootLineData) {
754  $fullRootLineByUid[$rootLineData['uid']] = $rootLineData;
755  }
756 
757  foreach ($this->rootLine as $level => $dataArray) {
758  $currentUid = $dataArray['uid'];
759 
760  if (!array_key_exists($currentUid, $fullRootLineByUid)) {
761  throw new \RuntimeException(sprintf('The full rootLine does not contain data for the page with the uid %d that is contained in the template rootline.', $currentUid), 1370419654);
762  }
763 
764  $this->rootLine[$level] = $fullRootLineByUid[$currentUid];
765  }
766  }
767 
778  public function includeStaticTypoScriptSources($idList, $templateID, $pid, $row)
779  {
780  // Static Template Records (static_template): include_static is a list of static templates to include
781  // Call function for link rendering:
782  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSources'])) {
783  $_params = array(
784  'idList' => &$idList,
785  'templateId' => &$templateID,
786  'pid' => &$pid,
787  'row' => &$row
788  );
789  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSources'] as $_funcRef) {
790  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
791  }
792  }
793  // If "Include before all static templates if root-flag is set" is set:
794  if ($row['static_file_mode'] == 3 && substr($templateID, 0, 4) == 'sys_' && $row['root']) {
795  $this->addExtensionStatics($idList, $templateID, $pid, $row);
796  }
797  // Static Template Files (Text files from extensions): include_static_file is a list of static files to include (from extensions)
798  if (trim($row['include_static_file'])) {
799  $include_static_fileArr = GeneralUtility::trimExplode(',', $row['include_static_file'], true);
800  // Traversing list
801  foreach ($include_static_fileArr as $ISF_file) {
802  if (substr($ISF_file, 0, 4) == 'EXT:') {
803  list($ISF_extKey, $ISF_localPath) = explode('/', substr($ISF_file, 4), 2);
804  if ((string)$ISF_extKey !== '' && ExtensionManagementUtility::isLoaded($ISF_extKey) && (string)$ISF_localPath !== '') {
805  $ISF_localPath = rtrim($ISF_localPath, '/') . '/';
806  $ISF_filePath = ExtensionManagementUtility::extPath($ISF_extKey) . $ISF_localPath;
807  if (@is_dir($ISF_filePath)) {
808  $mExtKey = str_replace('_', '', $ISF_extKey . '/' . $ISF_localPath);
809  $subrow = array(
810  'constants' => $this->getTypoScriptSourceFileContent($ISF_filePath, 'constants'),
811  'config' => $this->getTypoScriptSourceFileContent($ISF_filePath, 'setup'),
812  'include_static' => @file_exists(($ISF_filePath . 'include_static.txt')) ? implode(',', array_unique(GeneralUtility::intExplode(',', GeneralUtility::getUrl($ISF_filePath . 'include_static.txt')))) : '',
813  'include_static_file' => @file_exists(($ISF_filePath . 'include_static_file.txt')) ? implode(',', array_unique(explode(',', GeneralUtility::getUrl($ISF_filePath . 'include_static_file.txt')))) : '',
814  'title' => $ISF_file,
815  'uid' => $mExtKey
816  );
817  $subrow = $this->prependStaticExtra($subrow);
818  $this->processTemplate($subrow, $idList . ',ext_' . $mExtKey, $pid, 'ext_' . $mExtKey, $templateID, $ISF_filePath);
819  }
820  }
821  }
822  }
823  }
824  // If "Default (include before if root flag is set)" is set OR
825  // "Always include before this template record" AND root-flag are set
826  if ($row['static_file_mode'] == 1 || $row['static_file_mode'] == 0 && substr($templateID, 0, 4) == 'sys_' && $row['root']) {
827  $this->addExtensionStatics($idList, $templateID, $pid, $row);
828  }
829  // Include Static Template Records after all other TypoScript has been included.
830  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSourcesAtEnd'])) {
831  $_params = array(
832  'idList' => &$idList,
833  'templateId' => &$templateID,
834  'pid' => &$pid,
835  'row' => &$row
836  );
837  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['includeStaticTypoScriptSourcesAtEnd'] as $_funcRef) {
838  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
839  }
840  }
841  }
842 
851  protected function getTypoScriptSourceFileContent($filePath, $baseName)
852  {
853  $extensions = array('.ts', '.txt');
854  foreach ($extensions as $extension) {
855  $fileName = $filePath . $baseName . $extension;
856  if (@file_exists($fileName)) {
857  return GeneralUtility::getUrl($fileName);
858  }
859  }
860  return '';
861  }
862 
874  public function addExtensionStatics($idList, $templateID, $pid, $row)
875  {
876  $this->extensionStaticsProcessed = true;
877 
878  // @todo Change to use new API
879  foreach ($GLOBALS['TYPO3_LOADED_EXT'] as $extKey => $files) {
880  if ((is_array($files) || $files instanceof \ArrayAccess) && ($files['ext_typoscript_constants.txt'] || $files['ext_typoscript_setup.txt'])) {
881  $mExtKey = str_replace('_', '', $extKey);
882  $subrow = array(
883  'constants' => $files['ext_typoscript_constants.txt'] ? GeneralUtility::getUrl($files['ext_typoscript_constants.txt']) : '',
884  'config' => $files['ext_typoscript_setup.txt'] ? GeneralUtility::getUrl($files['ext_typoscript_setup.txt']) : '',
885  'title' => $extKey,
886  'uid' => $mExtKey
887  );
888  $subrow = $this->prependStaticExtra($subrow);
889  $extPath = ExtensionManagementUtility::extPath($extKey);
890  $this->processTemplate($subrow, $idList . ',ext_' . $mExtKey, $pid, 'ext_' . $mExtKey, $templateID, $extPath);
891  }
892  }
893  }
894 
905  public function prependStaticExtra($subrow)
906  {
907  // the identifier can be "43" if coming from "static template" extension or a path like "cssstyledcontent/static/"
908  $identifier = $subrow['uid'];
909  $subrow['config'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.'][$identifier];
910  $subrow['constants'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants.'][$identifier];
911  // if this is a template of type "default content rendering", also see if other extensions have added their TypoScript that should be included after the content definitions
912  if (in_array($identifier, $GLOBALS['TYPO3_CONF_VARS']['FE']['contentRenderingTemplates'], true)) {
913  $subrow['config'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup.']['defaultContentRendering'];
914  $subrow['constants'] .= $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants.']['defaultContentRendering'];
915  }
916  return $subrow;
917  }
918 
926  public function versionOL(&$row)
927  {
928  // Distinguish frontend and backend call:
929  // To do the fronted call a full frontend is required, just checking for
930  // TYPO3_MODE === 'FE' is not enough. This could otherwise lead to fatals in
931  // eId scripts that run in frontend scope, but do not have a full blown frontend.
932  if (is_object($this->getTypoScriptFrontendController()) && property_exists($this->getTypoScriptFrontendController(), 'sys_page') && method_exists($this->getTypoScriptFrontendController()->sys_page, 'versionOL')) {
933  // Frontend
934  $this->getTypoScriptFrontendController()->sys_page->versionOL('sys_template', $row);
935  } else {
936  // Backend
937  BackendUtility::workspaceOL('sys_template', $row);
938  }
939  }
940 
941  /*******************************************************************
942  *
943  * Parsing TypoScript code text from Template Records into PHP array
944  *
945  *******************************************************************/
953  public function generateConfig()
954  {
955  // Add default TS for all code types
956  $this->addDefaultTypoScript();
957 
958  // Parse the TypoScript code text for include-instructions!
959  $this->processIncludes();
960  // These vars are also set lateron...
961  $this->setup['sitetitle'] = $this->sitetitle;
962  // ****************************
963  // Parse TypoScript Constants
964  // ****************************
965  // Initialize parser and match-condition classes:
967  $constants = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
968  $constants->breakPointLN = (int)$this->ext_constants_BRP;
969  $constants->setup = $this->mergeConstantsFromPageTSconfig(array());
971  $matchObj = GeneralUtility::makeInstance(ConditionMatcher::class);
972  $matchObj->setSimulateMatchConditions($this->matchAlternative);
973  $matchObj->setSimulateMatchResult((bool)$this->matchAll);
974  // Traverse constants text fields and parse them
975  foreach ($this->constants as $str) {
976  $constants->parse($str, $matchObj);
977  }
978  // Read out parse errors if any
979  $this->parserErrors['constants'] = $constants->errors;
980  // Then flatten the structure from a multi-dim array to a single dim array with all constants listed as key/value pairs (ready for substitution)
981  $this->flatSetup = array();
982  $this->flattenSetup($constants->setup, '', '');
983  // ***********************************************
984  // Parse TypoScript Setup (here called "config")
985  // ***********************************************
986  // Initialize parser and match-condition classes:
988  $config = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
989  $config->breakPointLN = (int)$this->ext_config_BRP;
990  $config->regLinenumbers = $this->ext_regLinenumbers;
991  $config->regComments = $this->ext_regComments;
992  $config->setup = $this->setup;
993  // Transfer information about conditions found in "Constants" and which of them returned TRUE.
994  $config->sections = $constants->sections;
995  $config->sectionsMatch = $constants->sectionsMatch;
996  // Traverse setup text fields and concatenate them into one, single string separated by a [GLOBAL] condition
997  $all = '';
998  foreach ($this->config as $str) {
999  $all .= '
1000 [GLOBAL]
1001 ' . $str;
1002  }
1003  // Substitute constants in the Setup code:
1004  if ($this->tt_track) {
1005  $this->getTimeTracker()->push('Substitute Constants (' . count($this->flatSetup) . ')');
1006  }
1007  $all = $this->substituteConstants($all);
1008  if ($this->tt_track) {
1009  $this->getTimeTracker()->pull();
1010  }
1011 
1012  // Searching for possible unsubstituted constants left (only for information)
1013  if ($this->verbose) {
1014  if (strstr($all, '{$')) {
1015  $theConstList = array();
1016  $findConst = explode('{$', $all);
1017  array_shift($findConst);
1018  foreach ($findConst as $constVal) {
1019  $constLen = MathUtility::forceIntegerInRange(strcspn($constVal, '}'), 0, 50);
1020  $theConstList[] = '{$' . substr($constVal, 0, ($constLen + 1));
1021  }
1022  if ($this->tt_track) {
1023  $this->getTimeTracker()->setTSlogMessage(implode(', ', $theConstList) . ': Constants may remain un-substituted!!', 2);
1024  }
1025  }
1026  }
1027 
1028  // Logging the textual size of the TypoScript Setup field text with all constants substituted:
1029  if ($this->tt_track) {
1030  $this->getTimeTracker()->setTSlogMessage('TypoScript template size as textfile: ' . strlen($all) . ' bytes');
1031  }
1032  // Finally parse the Setup field TypoScript code (where constants are now substituted)
1033  $config->parse($all, $matchObj);
1034  // Read out parse errors if any
1035  $this->parserErrors['config'] = $config->errors;
1036  // Transfer the TypoScript array from the parser object to the internal $this->setup array:
1037  $this->setup = $config->setup;
1038  if ($this->backend_info) {
1039  // Used for backend purposes only
1040  $this->setup_constants = $constants->setup;
1041  }
1042  // ****************************************************************
1043  // Final processing of the $this->setup TypoScript Template array
1044  // Basically: This is unsetting/setting of certain reserved keys.
1045  // ****************************************************************
1046  // These vars are allready set after 'processTemplate', but because $config->setup overrides them (in the line above!), we set them again. They are not changed compared to the value they had in the top of the page!
1047  unset($this->setup['sitetitle']);
1048  unset($this->setup['sitetitle.']);
1049  $this->setup['sitetitle'] = $this->sitetitle;
1050  // Unsetting some vars...
1051  unset($this->setup['types.']);
1052  unset($this->setup['types']);
1053  if (is_array($this->setup)) {
1054  foreach ($this->setup as $key => $value) {
1055  if ($value == 'PAGE') {
1056  // Set the typeNum of the current page object:
1057  if (isset($this->setup[$key . '.']['typeNum'])) {
1058  $typeNum = $this->setup[$key . '.']['typeNum'];
1059  $this->setup['types.'][$typeNum] = $key;
1060  } elseif (!isset($this->setup['types.'][0]) || !$this->setup['types.'][0]) {
1061  $this->setup['types.'][0] = $key;
1062  }
1063  }
1064  }
1065  }
1066  unset($this->setup['temp.']);
1067  unset($constants);
1068  // Storing the conditions found/matched information:
1069  $this->sections = $config->sections;
1070  $this->sectionsMatch = $config->sectionsMatch;
1071  }
1072 
1081  public function processIncludes()
1082  {
1083  if ($this->processIncludesHasBeenRun) {
1084  return;
1085  }
1086 
1087  $paths = $this->templateIncludePaths;
1088  $files = array();
1089  foreach ($this->constants as &$value) {
1090  $includeData = Parser\TypoScriptParser::checkIncludeLines($value, 1, true, array_shift($paths));
1091  $files = array_merge($files, $includeData['files']);
1092  $value = $includeData['typoscript'];
1093  }
1094  unset($value);
1095  $paths = $this->templateIncludePaths;
1096  foreach ($this->config as &$value) {
1097  $includeData = Parser\TypoScriptParser::checkIncludeLines($value, 1, true, array_shift($paths));
1098  $files = array_merge($files, $includeData['files']);
1099  $value = $includeData['typoscript'];
1100  }
1101  unset($value);
1102 
1103  if (!empty($files)) {
1104  $files = array_unique($files);
1105  foreach ($files as $file) {
1106  $this->rowSum[] = array($file, filemtime($file));
1107  }
1108  }
1109 
1110  $this->processIncludesHasBeenRun = true;
1111  }
1112 
1120  public function mergeConstantsFromPageTSconfig($constArray)
1121  {
1122  $TSdataArray = array();
1123  // Setting default configuration:
1124  $TSdataArray[] = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'];
1125  for ($a = 0; $a <= $this->outermostRootlineIndexWithTemplate; $a++) {
1126  $TSdataArray[] = $this->absoluteRootLine[$a]['TSconfig'];
1127  }
1128  // Parsing the user TS (or getting from cache)
1129  $TSdataArray = Parser\TypoScriptParser::checkIncludeLines_array($TSdataArray);
1130  $userTS = implode(LF . '[GLOBAL]' . LF, $TSdataArray);
1132  $parseObj = GeneralUtility::makeInstance(Parser\TypoScriptParser::class);
1133  $parseObj->parse($userTS);
1134  if (is_array($parseObj->setup['TSFE.']['constants.'])) {
1135  ArrayUtility::mergeRecursiveWithOverrule($constArray, $parseObj->setup['TSFE.']['constants.']);
1136  }
1137  return $constArray;
1138  }
1139 
1148  public function flattenSetup($setupArray, $prefix)
1149  {
1150  if (is_array($setupArray)) {
1151  foreach ($setupArray as $key => $val) {
1152  if ($prefix || !StringUtility::beginsWith($key, 'TSConstantEditor')) {
1153  // We don't want 'TSConstantEditor' in the flattend setup on the first level (190201)
1154  if (is_array($val)) {
1155  $this->flattenSetup($val, $prefix . $key);
1156  } else {
1157  $this->flatSetup[$prefix . $key] = $val;
1158  }
1159  }
1160  }
1161  }
1162  }
1163 
1171  public function substituteConstants($all)
1172  {
1173  if ($this->tt_track) {
1174  $this->getTimeTracker()->setTSlogMessage('Constants to substitute: ' . count($this->flatSetup));
1175  }
1176  $noChange = false;
1177  // Recursive substitution of constants (up to 10 nested levels)
1178  for ($i = 0; $i < 10 && !$noChange; $i++) {
1179  $old_all = $all;
1180  $all = preg_replace_callback('/\\{\\$(.[^}]*)\\}/', array($this, 'substituteConstantsCallBack'), $all);
1181  if ($old_all == $all) {
1182  $noChange = true;
1183  }
1184  }
1185  return $all;
1186  }
1187 
1195  public function substituteConstantsCallBack($matches)
1196  {
1197  // Replace {$CONST} if found in $this->flatSetup, else leave unchanged
1198  return isset($this->flatSetup[$matches[1]]) && !is_array($this->flatSetup[$matches[1]]) ? $this->flatSetup[$matches[1]] : $matches[0];
1199  }
1200 
1201  /*******************************************************************
1202  *
1203  * Various API functions, used from elsewhere in the frontend classes
1204  *
1205  *******************************************************************/
1216  public function splitConfArray($conf, $splitCount)
1217  {
1218  // Initialize variables:
1219  $splitCount = (int)$splitCount;
1220  $conf2 = array();
1221  if ($splitCount && is_array($conf)) {
1222  // Initialize output to carry at least the keys:
1223  for ($aKey = 0; $aKey < $splitCount; $aKey++) {
1224  $conf2[$aKey] = array();
1225  }
1226  // Recursive processing of array keys:
1227  foreach ($conf as $cKey => $val) {
1228  if (is_array($val)) {
1229  $tempConf = $this->splitConfArray($val, $splitCount);
1230  foreach ($tempConf as $aKey => $val2) {
1231  $conf2[$aKey][$cKey] = $val2;
1232  }
1233  } else {
1234  // Splitting of all values on this level of the TypoScript object tree:
1235  if ($cKey === 'noTrimWrap' || (!strstr($val, '|*|') && !strstr($val, '||'))) {
1236  for ($aKey = 0; $aKey < $splitCount; $aKey++) {
1237  $conf2[$aKey][$cKey] = $val;
1238  }
1239  } else {
1240  $main = explode('|*|', $val);
1241  $lastC = 0;
1242  $middleC = 0;
1243  $firstC = 0;
1244  if ($main[0]) {
1245  $first = explode('||', $main[0]);
1246  $firstC = count($first);
1247  }
1248  $middle = array();
1249  if ($main[1]) {
1250  $middle = explode('||', $main[1]);
1251  $middleC = count($middle);
1252  }
1253  $last = array();
1254  $value = '';
1255  if ($main[2]) {
1256  $last = explode('||', $main[2]);
1257  $lastC = count($last);
1258  $value = $last[0];
1259  }
1260  for ($aKey = 0; $aKey < $splitCount; $aKey++) {
1261  if ($firstC && isset($first[$aKey])) {
1262  $value = $first[$aKey];
1263  } elseif ($middleC) {
1264  $value = $middle[($aKey - $firstC) % $middleC];
1265  }
1266  if ($lastC && $lastC >= $splitCount - $aKey) {
1267  $value = $last[$lastC - ($splitCount - $aKey)];
1268  }
1269  $conf2[$aKey][$cKey] = trim($value);
1270  }
1271  }
1272  }
1273  }
1274  }
1275  return $conf2;
1276  }
1277 
1284  public function getFileName($fileFromSetup)
1285  {
1286  $file = trim($fileFromSetup);
1287  if (!$file) {
1288  return null;
1289  } elseif (strpos($file, '../') !== false) {
1290  if ($this->tt_track) {
1291  $this->getTimeTracker()->setTSlogMessage('File path "' . $file . '" contained illegal string "../"!', 3);
1292  }
1293  return null;
1294  }
1295  // Cache
1296  $hash = md5($file);
1297  if (isset($this->fileCache[$hash])) {
1298  return $this->fileCache[$hash];
1299  }
1300 
1301  // if this is an URL, it can be returned directly
1302  $urlScheme = parse_url($file, PHP_URL_SCHEME);
1303  if ($urlScheme === 'https' || $urlScheme === 'http' || is_file(PATH_site . $file)) {
1304  return $file;
1305  }
1306 
1307  // this call also resolves EXT:myext/ files
1308  $file = GeneralUtility::getFileAbsFileName($file);
1309  if (!$file) {
1310  if ($this->tt_track) {
1311  $this->getTimeTracker()->setTSlogMessage('File "' . $fileFromSetup . '" was not found!', 3);
1312  }
1313  return null;
1314  }
1315 
1316  $file = PathUtility::stripPathSitePrefix($file);
1317 
1318  // Check if the found file is in the allowed paths
1319  foreach ($this->allowedPaths as $val) {
1320  if (GeneralUtility::isFirstPartOfStr($file, $val)) {
1321  $this->fileCache[$hash] = $file;
1322  return $file;
1323  }
1324  }
1325 
1326  if ($this->tt_track) {
1327  $this->getTimeTracker()->setTSlogMessage('"' . $file . '" was not located in the allowed paths: (' . implode(',', $this->allowedPaths) . ')', 3);
1328  }
1329  return null;
1330  }
1331 
1342  public function printTitle($pageTitle, $noTitle = false, $showTitleFirst = false, $pageTitleSeparator = '')
1343  {
1344  $siteTitle = trim($this->setup['sitetitle']);
1345  $pageTitle = $noTitle ? '' : $pageTitle;
1346  if ($showTitleFirst) {
1347  $temp = $siteTitle;
1348  $siteTitle = $pageTitle;
1349  $pageTitle = $temp;
1350  }
1351  // only show a separator if there are both site title and page title
1352  if ($pageTitle === '' || $siteTitle === '') {
1353  $pageTitleSeparator = '';
1354  // use the default separator if non given
1355  } elseif (empty($pageTitleSeparator)) {
1356  $pageTitleSeparator = ': ';
1357  }
1358  return $siteTitle . $pageTitleSeparator . $pageTitle;
1359  }
1360 
1369  public function fileContent($fileName)
1370  {
1371  $fileName = $this->getFileName($fileName);
1372  if ($fileName) {
1373  return GeneralUtility::getUrl($fileName);
1374  }
1375  return null;
1376  }
1377 
1387  public function wrap($content, $wrap)
1388  {
1390  if ($wrap) {
1391  $wrapArr = explode('|', $wrap);
1392  return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
1393  } else {
1394  return $content;
1395  }
1396  }
1397 
1405  public function removeQueryString($url)
1406  {
1407  if (substr($url, -1) == '?') {
1408  return substr($url, 0, -1);
1409  } else {
1410  return $url;
1411  }
1412  }
1413 
1423  public static function sortedKeyList($setupArr, $acceptOnlyProperties = false)
1424  {
1425  $keyArr = array();
1426  $setupArrKeys = array_keys($setupArr);
1427  foreach ($setupArrKeys as $key) {
1428  if ($acceptOnlyProperties || MathUtility::canBeInterpretedAsInteger($key)) {
1429  $keyArr[] = (int)$key;
1430  }
1431  }
1432  $keyArr = array_unique($keyArr);
1433  sort($keyArr);
1434  return $keyArr;
1435  }
1436 
1443  public function getRootlineLevel($list)
1444  {
1445  $idx = 0;
1446  foreach ($this->rootLine as $page) {
1447  if (GeneralUtility::inList($list, $page['uid'])) {
1448  return $idx;
1449  }
1450  $idx++;
1451  }
1452  return false;
1453  }
1454 
1455  /*******************************************************************
1456  *
1457  * Functions for creating links
1458  *
1459  *******************************************************************/
1477  public function linkData($page, $oTarget, $no_cache, $script, $overrideArray = null, $addParams = '', $typeOverride = '', $targetDomain = '')
1478  {
1479  $LD = array();
1480  // Overriding some fields in the page record and still preserves the values by adding them as parameters. Little strange function.
1481  if (is_array($overrideArray)) {
1482  foreach ($overrideArray as $theKey => $theNewVal) {
1483  $addParams .= '&real_' . $theKey . '=' . rawurlencode($page[$theKey]);
1484  $page[$theKey] = $theNewVal;
1485  }
1486  }
1487  // Adding Mount Points, "&MP=", parameter for the current page if any is set:
1488  if (!strstr($addParams, '&MP=')) {
1489  // Looking for hardcoded defaults:
1490  if (trim($this->getTypoScriptFrontendController()->MP_defaults[$page['uid']])) {
1491  $addParams .= '&MP=' . rawurlencode(trim($this->getTypoScriptFrontendController()->MP_defaults[$page['uid']]));
1492  } elseif ($this->getTypoScriptFrontendController()->config['config']['MP_mapRootPoints']) {
1493  // Else look in automatically created map:
1494  $m = $this->getFromMPmap($page['uid']);
1495  if ($m) {
1496  $addParams .= '&MP=' . rawurlencode($m);
1497  }
1498  }
1499  }
1500  // Setting ID/alias:
1501  if (!$script) {
1502  $script = $this->getTypoScriptFrontendController()->config['mainScript'];
1503  }
1504  if ($page['alias']) {
1505  $LD['url'] = $script . '?id=' . rawurlencode($page['alias']);
1506  } else {
1507  $LD['url'] = $script . '?id=' . $page['uid'];
1508  }
1509  // Setting target
1510  $LD['target'] = trim($page['target']) ?: $oTarget;
1511  // typeNum
1512  $typeNum = $this->setup[$LD['target'] . '.']['typeNum'];
1513  if (!MathUtility::canBeInterpretedAsInteger($typeOverride) && (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue']) {
1514  $typeOverride = (int)$this->getTypoScriptFrontendController()->config['config']['forceTypeValue'];
1515  }
1516  if ((string)$typeOverride !== '') {
1517  $typeNum = $typeOverride;
1518  }
1519  // Override...
1520  if ($typeNum) {
1521  $LD['type'] = '&type=' . (int)$typeNum;
1522  } else {
1523  $LD['type'] = '';
1524  }
1525  // Preserving the type number.
1526  $LD['orig_type'] = $LD['type'];
1527  // noCache
1528  $LD['no_cache'] = trim($page['no_cache']) || $no_cache ? '&no_cache=1' : '';
1529  // linkVars
1530  if ($addParams) {
1531  $LD['linkVars'] = GeneralUtility::implodeArrayForUrl('', GeneralUtility::explodeUrl2Array($this->getTypoScriptFrontendController()->linkVars . $addParams), '', false, true);
1532  } else {
1533  $LD['linkVars'] = $this->getTypoScriptFrontendController()->linkVars;
1534  }
1535  // Add absRefPrefix if exists.
1536  $LD['url'] = $this->getTypoScriptFrontendController()->absRefPrefix . $LD['url'];
1537  // If the special key 'sectionIndex_uid' (added 'manually' in tslib/menu.php to the page-record) is set, then the link jumps directly to a section on the page.
1538  $LD['sectionIndex'] = $page['sectionIndex_uid'] ? '#c' . $page['sectionIndex_uid'] : '';
1539  // Compile the normal total url
1540  $LD['totalURL'] = $this->removeQueryString(($LD['url'] . $LD['type'] . $LD['no_cache'] . $LD['linkVars'] . $this->getTypoScriptFrontendController()->getMethodUrlIdToken)) . $LD['sectionIndex'];
1541  // Call post processing function for link rendering:
1542  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['linkData-PostProc'])) {
1543  $_params = array(
1544  'LD' => &$LD,
1545  'args' => array('page' => $page, 'oTarget' => $oTarget, 'no_cache' => $no_cache, 'script' => $script, 'overrideArray' => $overrideArray, 'addParams' => $addParams, 'typeOverride' => $typeOverride, 'targetDomain' => $targetDomain),
1546  'typeNum' => $typeNum
1547  );
1548  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tstemplate.php']['linkData-PostProc'] as $_funcRef) {
1549  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1550  }
1551  }
1552  // Return the LD-array
1553  return $LD;
1554  }
1555 
1565  public function getFromMPmap($pageId = 0)
1566  {
1567  // Create map if not found already:
1568  if (!is_array($this->MPmap)) {
1569  $this->MPmap = array();
1570  $rootPoints = GeneralUtility::trimExplode(',', strtolower($this->getTypoScriptFrontendController()->config['config']['MP_mapRootPoints']), true);
1571  // Traverse rootpoints:
1572  foreach ($rootPoints as $p) {
1573  $initMParray = array();
1574  if ($p == 'root') {
1575  $p = $this->rootLine[0]['uid'];
1576  if ($this->rootLine[0]['_MOUNT_OL'] && $this->rootLine[0]['_MP_PARAM']) {
1577  $initMParray[] = $this->rootLine[0]['_MP_PARAM'];
1578  }
1579  }
1580  $this->initMPmap_create($p, $initMParray);
1581  }
1582  }
1583  // Finding MP var for Page ID:
1584  if ($pageId) {
1585  if (is_array($this->MPmap[$pageId]) && !empty($this->MPmap[$pageId])) {
1586  return implode(',', $this->MPmap[$pageId]);
1587  }
1588  }
1589  return '';
1590  }
1591 
1601  public function initMPmap_create($id, $MP_array = array(), $level = 0)
1602  {
1603  $id = (int)$id;
1604  if ($id <= 0) {
1605  return;
1606  }
1607  // First level, check id
1608  if (!$level) {
1609  // Find mount point if any:
1610  $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($id);
1611  // Overlay mode:
1612  if (is_array($mount_info) && $mount_info['overlay']) {
1613  $MP_array[] = $mount_info['MPvar'];
1614  $id = $mount_info['mount_pid'];
1615  }
1616  // Set mapping information for this level:
1617  $this->MPmap[$id] = $MP_array;
1618  // Normal mode:
1619  if (is_array($mount_info) && !$mount_info['overlay']) {
1620  $MP_array[] = $mount_info['MPvar'];
1621  $id = $mount_info['mount_pid'];
1622  }
1623  }
1624  if ($id && $level < 20) {
1625  $nextLevelAcc = array();
1626  // Select and traverse current level pages:
1627  $res = $this->getDatabaseConnection()->exec_SELECTquery('uid,pid,doktype,mount_pid,mount_pid_ol', 'pages', 'pid=' . (int)$id . ' AND deleted=0 AND doktype<>' . PageRepository::DOKTYPE_RECYCLER . ' AND doktype<>' . PageRepository::DOKTYPE_BE_USER_SECTION);
1628  while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
1629  // Find mount point if any:
1630  $next_id = $row['uid'];
1631  $next_MP_array = $MP_array;
1632  $mount_info = $this->getTypoScriptFrontendController()->sys_page->getMountPointInfo($next_id, $row);
1633  // Overlay mode:
1634  if (is_array($mount_info) && $mount_info['overlay']) {
1635  $next_MP_array[] = $mount_info['MPvar'];
1636  $next_id = $mount_info['mount_pid'];
1637  }
1638  if (!isset($this->MPmap[$next_id])) {
1639  // Set mapping information for this level:
1640  $this->MPmap[$next_id] = $next_MP_array;
1641  // Normal mode:
1642  if (is_array($mount_info) && !$mount_info['overlay']) {
1643  $next_MP_array[] = $mount_info['MPvar'];
1644  $next_id = $mount_info['mount_pid'];
1645  }
1646  // Register recursive call
1647  // (have to do it this way since ALL of the current level should be registered BEFORE the sublevel at any time)
1648  $nextLevelAcc[] = array($next_id, $next_MP_array);
1649  }
1650  }
1651  $this->getDatabaseConnection()->sql_free_result($res);
1652  // Call recursively, if any:
1653  foreach ($nextLevelAcc as $pSet) {
1654  $this->initMPmap_create($pSet[0], $pSet[1], $level + 1);
1655  }
1656  }
1657  }
1658 
1667  protected function addDefaultTypoScript()
1668  {
1669  // Add default TS for all code types, if not done already
1670  if (!$this->isDefaultTypoScriptAdded) {
1671  // adding default setup and constants
1672  // defaultTypoScript_setup is *very* unlikely to be empty
1673  // the count of elements in ->constants, ->config and ->templateIncludePaths have to be in sync
1674  array_unshift($this->constants, (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants']);
1675  array_unshift($this->config, (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup']);
1676  array_unshift($this->templateIncludePaths, '');
1677  // prepare a proper entry to hierachyInfo (used by TemplateAnalyzer in BE)
1678  $rootTemplateId = $this->hierarchyInfo[count($this->hierarchyInfo)-1]['templateID'];
1679  $defaultTemplateInfo = array(
1680  'root' => '',
1681  'next' => '',
1682  'clConst' => '',
1683  'clConf' => '',
1684  'templateID' => '_defaultTypoScript_',
1685  'templateParent' => $rootTemplateId,
1686  'title' => 'SYS:TYPO3_CONF_VARS:FE:defaultTypoScript',
1687  'uid' => '_defaultTypoScript_',
1688  'pid' => '',
1689  'configLines' => substr_count((string)$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'], LF) + 1
1690  );
1691  // push info to information arrays used in BE by TemplateTools (Analyzer)
1692  array_unshift($this->clearList_const, $defaultTemplateInfo['uid']);
1693  array_unshift($this->clearList_setup, $defaultTemplateInfo['uid']);
1694  array_unshift($this->hierarchyInfo, $defaultTemplateInfo);
1695  $this->isDefaultTypoScriptAdded = true;
1696  }
1697  }
1698 
1702  protected function getDatabaseConnection()
1703  {
1704  return $GLOBALS['TYPO3_DB'];
1705  }
1706 
1710  protected function getTypoScriptFrontendController()
1711  {
1712  return $GLOBALS['TSFE'];
1713  }
1714 
1718  protected function getTimeTracker()
1719  {
1720  return $GLOBALS['TT'];
1721  }
1722 }