TYPO3 CMS  TYPO3_7-6
ContentObjectRenderer.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 
52 
63 {
67  public $align = [
68  'center',
69  'right',
70  'left'
71  ];
72 
78  public $stdWrapOrder = [
79  'stdWrapPreProcess' => 'hook',
80  // this is a placeholder for the first Hook
81  'cacheRead' => 'hook',
82  // this is a placeholder for checking if the content is available in cache
83  'setContentToCurrent' => 'boolean',
84  'setContentToCurrent.' => 'array',
85  'addPageCacheTags' => 'string',
86  'addPageCacheTags.' => 'array',
87  'setCurrent' => 'string',
88  'setCurrent.' => 'array',
89  'lang.' => 'array',
90  'data' => 'getText',
91  'data.' => 'array',
92  'field' => 'fieldName',
93  'field.' => 'array',
94  'current' => 'boolean',
95  'current.' => 'array',
96  'cObject' => 'cObject',
97  'cObject.' => 'array',
98  'numRows.' => 'array',
99  'filelist' => 'dir',
100  'filelist.' => 'array',
101  'preUserFunc' => 'functionName',
102  'stdWrapOverride' => 'hook',
103  // this is a placeholder for the second Hook
104  'override' => 'string',
105  'override.' => 'array',
106  'preIfEmptyListNum' => 'listNum',
107  'preIfEmptyListNum.' => 'array',
108  'ifNull' => 'string',
109  'ifNull.' => 'array',
110  'ifEmpty' => 'string',
111  'ifEmpty.' => 'array',
112  'ifBlank' => 'string',
113  'ifBlank.' => 'array',
114  'listNum' => 'listNum',
115  'listNum.' => 'array',
116  'trim' => 'boolean',
117  'trim.' => 'array',
118  'strPad.' => 'array',
119  'stdWrap' => 'stdWrap',
120  'stdWrap.' => 'array',
121  'stdWrapProcess' => 'hook',
122  // this is a placeholder for the third Hook
123  'required' => 'boolean',
124  'required.' => 'array',
125  'if.' => 'array',
126  'fieldRequired' => 'fieldName',
127  'fieldRequired.' => 'array',
128  'csConv' => 'string',
129  'csConv.' => 'array',
130  'parseFunc' => 'objectpath',
131  'parseFunc.' => 'array',
132  'HTMLparser' => 'boolean',
133  'HTMLparser.' => 'array',
134  'split.' => 'array',
135  'replacement.' => 'array',
136  'prioriCalc' => 'boolean',
137  'prioriCalc.' => 'array',
138  'char' => 'integer',
139  'char.' => 'array',
140  'intval' => 'boolean',
141  'intval.' => 'array',
142  'hash' => 'string',
143  'hash.' => 'array',
144  'round' => 'boolean',
145  'round.' => 'array',
146  'numberFormat.' => 'array',
147  'expandList' => 'boolean',
148  'expandList.' => 'array',
149  'date' => 'dateconf',
150  'date.' => 'array',
151  'strtotime' => 'strtotimeconf',
152  'strtotime.' => 'array',
153  'strftime' => 'strftimeconf',
154  'strftime.' => 'array',
155  'age' => 'boolean',
156  'age.' => 'array',
157  'case' => 'case',
158  'case.' => 'array',
159  'bytes' => 'boolean',
160  'bytes.' => 'array',
161  'substring' => 'parameters',
162  'substring.' => 'array',
163  'removeBadHTML' => 'boolean',
164  'removeBadHTML.' => 'array',
165  'cropHTML' => 'crop',
166  'cropHTML.' => 'array',
167  'stripHtml' => 'boolean',
168  'stripHtml.' => 'array',
169  'crop' => 'crop',
170  'crop.' => 'array',
171  'rawUrlEncode' => 'boolean',
172  'rawUrlEncode.' => 'array',
173  'htmlSpecialChars' => 'boolean',
174  'htmlSpecialChars.' => 'array',
175  'encodeForJavaScriptValue' => 'boolean',
176  'encodeForJavaScriptValue.' => 'array',
177  'doubleBrTag' => 'string',
178  'doubleBrTag.' => 'array',
179  'br' => 'boolean',
180  'br.' => 'array',
181  'brTag' => 'string',
182  'brTag.' => 'array',
183  'encapsLines.' => 'array',
184  'keywords' => 'boolean',
185  'keywords.' => 'array',
186  'innerWrap' => 'wrap',
187  'innerWrap.' => 'array',
188  'innerWrap2' => 'wrap',
189  'innerWrap2.' => 'array',
190  'fontTag' => 'wrap',
191  'fontTag.' => 'array',
192  'addParams.' => 'array',
193  'textStyle.' => 'array',
194  'tableStyle.' => 'array',
195  'filelink.' => 'array',
196  'preCObject' => 'cObject',
197  'preCObject.' => 'array',
198  'postCObject' => 'cObject',
199  'postCObject.' => 'array',
200  'wrapAlign' => 'align',
201  'wrapAlign.' => 'array',
202  'typolink.' => 'array',
203  'TCAselectItem.' => 'array',
204  'space' => 'space',
205  'space.' => 'array',
206  'spaceBefore' => 'int',
207  'spaceBefore.' => 'array',
208  'spaceAfter' => 'int',
209  'spaceAfter.' => 'array',
210  'wrap' => 'wrap',
211  'wrap.' => 'array',
212  'noTrimWrap' => 'wrap',
213  'noTrimWrap.' => 'array',
214  'wrap2' => 'wrap',
215  'wrap2.' => 'array',
216  'dataWrap' => 'dataWrap',
217  'dataWrap.' => 'array',
218  'prepend' => 'cObject',
219  'prepend.' => 'array',
220  'append' => 'cObject',
221  'append.' => 'array',
222  'wrap3' => 'wrap',
223  'wrap3.' => 'array',
224  'orderedStdWrap' => 'stdWrap',
225  'orderedStdWrap.' => 'array',
226  'outerWrap' => 'wrap',
227  'outerWrap.' => 'array',
228  'insertData' => 'boolean',
229  'insertData.' => 'array',
230  'offsetWrap' => 'space',
231  'offsetWrap.' => 'array',
232  'postUserFunc' => 'functionName',
233  'postUserFuncInt' => 'functionName',
234  'prefixComment' => 'string',
235  'prefixComment.' => 'array',
236  'editIcons' => 'string',
237  'editIcons.' => 'array',
238  'editPanel' => 'boolean',
239  'editPanel.' => 'array',
240  'cacheStore' => 'hook',
241  // this is a placeholder for storing the content in cache
242  'stdWrapPostProcess' => 'hook',
243  // this is a placeholder for the last Hook
244  'debug' => 'boolean',
245  'debug.' => 'array',
246  'debugFunc' => 'boolean',
247  'debugFunc.' => 'array',
248  'debugData' => 'boolean',
249  'debugData.' => 'array'
250  ];
251 
257  protected $contentObjectClassMap = [];
258 
266  10 => [
267  'params' => '',
268  'ext' => 'gif'
269  ],
270  11 => [
271  'params' => '-colors 128',
272  'ext' => 'gif'
273  ],
274  12 => [
275  'params' => '-colors 64',
276  'ext' => 'gif'
277  ],
278  13 => [
279  'params' => '-colors 32',
280  'ext' => 'gif'
281  ],
282  14 => [
283  'params' => '-colors 16',
284  'ext' => 'gif'
285  ],
286  15 => [
287  'params' => '-colors 8',
288  'ext' => 'gif'
289  ],
290  20 => [
291  'params' => '-quality 100',
292  'ext' => 'jpg'
293  ],
294  21 => [
295  'params' => '-quality 90',
296  'ext' => 'jpg'
297  ],
298  22 => [
299  'params' => '-quality 80',
300  'ext' => 'jpg'
301  ],
302  23 => [
303  'params' => '-quality 70',
304  'ext' => 'jpg'
305  ],
306  24 => [
307  'params' => '-quality 60',
308  'ext' => 'jpg'
309  ],
310  25 => [
311  'params' => '-quality 50',
312  'ext' => 'jpg'
313  ],
314  26 => [
315  'params' => '-quality 40',
316  'ext' => 'jpg'
317  ],
318  27 => [
319  'params' => '-quality 30',
320  'ext' => 'jpg'
321  ],
322  28 => [
323  'params' => '-quality 20',
324  'ext' => 'jpg'
325  ],
326  30 => [
327  'params' => '-colors 256',
328  'ext' => 'png'
329  ],
330  31 => [
331  'params' => '-colors 128',
332  'ext' => 'png'
333  ],
334  32 => [
335  'params' => '-colors 64',
336  'ext' => 'png'
337  ],
338  33 => [
339  'params' => '-colors 32',
340  'ext' => 'png'
341  ],
342  34 => [
343  'params' => '-colors 16',
344  'ext' => 'png'
345  ],
346  35 => [
347  'params' => '-colors 8',
348  'ext' => 'png'
349  ],
350  39 => [
351  'params' => '',
352  'ext' => 'png'
353  ]
354  ];
355 
362  public $image_effects = [
363  1 => '-rotate 90',
364  2 => '-rotate 270',
365  3 => '-rotate 180',
366  10 => '-colorspace GRAY',
367  11 => '-sharpen 70',
368  20 => '-normalize',
369  23 => '-contrast',
370  25 => '-gamma 1.3',
371  26 => '-gamma 0.8'
372  ];
373 
383  public $data = [];
384 
388  protected $table = '';
389 
395  public $oldData = [];
396 
402  public $alternativeData = '';
403 
409  public $parameters = [];
410 
414  public $currentValKey = 'currentValue_kidjls9dksoje';
415 
422  public $currentRecord = '';
423 
430 
437 
444 
450  public $parentRecord = [];
451 
457  public $INT_include = 0;
458 
464  public $checkPid_cache = [];
465 
469  public $checkPid_badDoktypeList = '255';
470 
476  public $lastTypoLinkUrl = '';
477 
483  public $lastTypoLinkTarget = '';
484 
488  public $lastTypoLinkLD = [];
489 
495  public $substMarkerCache = [];
496 
502  public $recordRegister = [];
503 
509  protected $cObjHookObjectsRegistry = [];
510 
514  public $cObjHookObjectsArr = [];
515 
521  protected $stdWrapHookObjects = [];
522 
529 
533  protected $currentFile = null;
534 
539 
545  protected $userObjectType = false;
546 
550  protected $stopRendering = [];
551 
555  protected $stdWrapRecursionLevel = 0;
556 
561 
565  protected $templateService;
566 
578  const OBJECTTYPE_USER = 2;
579 
584  {
585  $this->typoScriptFrontendController = $typoScriptFrontendController;
586  $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
587  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
588  }
589 
597  public function __sleep()
598  {
599  $vars = get_object_vars($this);
600  unset($vars['typoScriptFrontendController']);
601  if ($this->currentFile instanceof FileReference) {
602  $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
603  } elseif ($this->currentFile instanceof File) {
604  $this->currentFile = 'File:' . $this->currentFile->getIdentifier();
605  } else {
606  unset($vars['currentFile']);
607  }
608  return array_keys($vars);
609  }
610 
616  public function __wakeup()
617  {
618  if (isset($GLOBALS['TSFE'])) {
619  $this->typoScriptFrontendController = $GLOBALS['TSFE'];
620  }
621  if ($this->currentFile !== null && is_string($this->currentFile)) {
622  list($objectType, $identifier) = explode(':', $this->currentFile, 2);
623  try {
624  if ($objectType === 'File') {
625  $this->currentFile = ResourceFactory::getInstance()->retrieveFileOrFolderObject($identifier);
626  } elseif ($objectType === 'FileReference') {
627  $this->currentFile = ResourceFactory::getInstance()->getFileReferenceObject($identifier);
628  }
629  } catch (ResourceDoesNotExistException $e) {
630  $this->currentFile = null;
631  }
632  }
633  }
634 
645  {
646  $this->contentObjectClassMap = $contentObjectClassMap;
647  }
648 
659  public function registerContentObjectClass($className, $contentObjectName)
660  {
661  $this->contentObjectClassMap[$contentObjectName] = $className;
662  }
663 
673  public function start($data, $table = '')
674  {
675  $this->data = $data;
676  $this->table = $table;
677  $this->currentRecord = $table !== '' ? $table . ':' . $this->data['uid'] : '';
678  $this->parameters = [];
679  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'])) {
680  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'] as $classArr) {
681  $this->cObjHookObjectsRegistry[$classArr[0]] = $classArr[1];
682  }
683  }
684  $this->stdWrapHookObjects = [];
685  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'])) {
686  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'] as $classData) {
687  $hookObject = GeneralUtility::getUserObj($classData);
688  if (!$hookObject instanceof ContentObjectStdWrapHookInterface) {
689  throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectStdWrapHookInterface::class, 1195043965);
690  }
691  $this->stdWrapHookObjects[] = $hookObject;
692  }
693  }
694  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'])) {
695  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'] as $classData) {
696  $postInitializationProcessor = GeneralUtility::getUserObj($classData);
697  if (!$postInitializationProcessor instanceof ContentObjectPostInitHookInterface) {
698  throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectPostInitHookInterface::class, 1274563549);
699  }
700  $postInitializationProcessor->postProcessContentObjectInitialization($this);
701  }
702  }
703  }
704 
710  public function getCurrentTable()
711  {
712  return $this->table;
713  }
714 
721  protected function getGetImgResourceHookObjects()
722  {
723  if (!isset($this->getImgResourceHookObjects)) {
724  $this->getImgResourceHookObjects = [];
725  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'])) {
726  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'] as $classData) {
727  $hookObject = GeneralUtility::getUserObj($classData);
728  if (!$hookObject instanceof ContentObjectGetImageResourceHookInterface) {
729  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetImageResourceHookInterface::class, 1218636383);
730  }
731  $this->getImgResourceHookObjects[] = $hookObject;
732  }
733  }
734  }
736  }
737 
747  public function setParent($data, $currentRecord)
748  {
749  $this->parentRecord = [
750  'data' => $data,
751  'currentRecord' => $currentRecord
752  ];
753  }
754 
755  /***********************************************
756  *
757  * CONTENT_OBJ:
758  *
759  ***********************************************/
768  public function getCurrentVal()
769  {
770  return $this->data[$this->currentValKey];
771  }
772 
780  public function setCurrentVal($value)
781  {
782  $this->data[$this->currentValKey] = $value;
783  }
784 
794  public function cObjGet($setup, $addKey = '')
795  {
796  if (!is_array($setup)) {
797  return '';
798  }
799  $sKeyArray = TemplateService::sortedKeyList($setup);
800  $content = '';
801  foreach ($sKeyArray as $theKey) {
802  $theValue = $setup[$theKey];
803  if ((int)$theKey && strpos($theKey, '.') === false) {
804  $conf = $setup[$theKey . '.'];
805  $content .= $this->cObjGetSingle($theValue, $conf, $addKey . $theKey);
806  }
807  }
808  return $content;
809  }
810 
820  public function cObjGetSingle($name, $conf, $TSkey = '__')
821  {
822  $content = '';
823  // Checking that the function is not called eternally. This is done by interrupting at a depth of 100
824  $this->getTypoScriptFrontendController()->cObjectDepthCounter--;
825  if ($this->getTypoScriptFrontendController()->cObjectDepthCounter > 0) {
826  $timeTracker = $this->getTimeTracker();
827  $name = trim($name);
828  if ($timeTracker->LR) {
829  $timeTracker->push($TSkey, $name);
830  }
831  // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
832  if ($name[0] === '<') {
833  $key = trim(substr($name, 1));
834  $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
835  // $name and $conf is loaded with the referenced values.
836  $confOverride = is_array($conf) ? $conf : [];
837  list($name, $conf) = $cF->getVal($key, $this->getTypoScriptFrontendController()->tmpl->setup);
838  $conf = array_replace_recursive(is_array($conf) ? $conf : [], $confOverride);
839  // Getting the cObject
840  $timeTracker->incStackPointer();
841  $content .= $this->cObjGetSingle($name, $conf, $key);
842  $timeTracker->decStackPointer();
843  } else {
844  $hooked = false;
845  // Application defined cObjects
846  if (!empty($this->cObjHookObjectsRegistry[$name])) {
847  if (empty($this->cObjHookObjectsArr[$name])) {
848  $this->cObjHookObjectsArr[$name] = GeneralUtility::getUserObj($this->cObjHookObjectsRegistry[$name]);
849  }
850  $hookObj = $this->cObjHookObjectsArr[$name];
851  if (method_exists($hookObj, 'cObjGetSingleExt')) {
852  $content .= $hookObj->cObjGetSingleExt($name, $conf, $TSkey, $this);
853  $hooked = true;
854  }
855  }
856  if (!$hooked) {
857  $contentObject = $this->getContentObject($name);
858  if ($contentObject) {
859  $content .= $this->render($contentObject, $conf);
860  } else {
861  // Call hook functions for extra processing
862  if ($name && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'])) {
863  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] as $classData) {
864  $hookObject = GeneralUtility::getUserObj($classData);
865  if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
866  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
867  }
869  $content .= $hookObject->getSingleContentObject($name, (array)$conf, $TSkey, $this);
870  }
871  } else {
872  // Log error in AdminPanel
873  $warning = sprintf('Content Object "%s" does not exist', $name);
874  $timeTracker->setTSlogMessage($warning, 2);
875  }
876  }
877  }
878  }
879  if ($timeTracker->LR) {
880  $timeTracker->pull($content);
881  }
882  }
883  // Increasing on exit...
884  $this->getTypoScriptFrontendController()->cObjectDepthCounter++;
885  return $content;
886  }
887 
897  public function getContentObject($name)
898  {
899  if (!isset($this->contentObjectClassMap[$name])) {
900  return null;
901  }
902  $fullyQualifiedClassName = $this->contentObjectClassMap[$name];
903  $contentObject = GeneralUtility::makeInstance($fullyQualifiedClassName, $this);
904  if (!($contentObject instanceof AbstractContentObject)) {
905  throw new ContentRenderingException(sprintf('Registered content object class name "%s" must be an instance of AbstractContentObject, but is not!', $fullyQualifiedClassName), 1422564295);
906  }
907  return $contentObject;
908  }
909 
910  /********************************************
911  *
912  * Functions rendering content objects (cObjects)
913  *
914  ********************************************/
915 
927  public function render(AbstractContentObject $contentObject, $configuration = [])
928  {
929  $content = '';
930 
931  // Evaluate possible cache and return
932  $cacheConfiguration = isset($configuration['cache.']) ? $configuration['cache.'] : null;
933  if ($cacheConfiguration !== null) {
934  unset($configuration['cache.']);
935  $cache = $this->getFromCache($cacheConfiguration);
936  if ($cache !== false) {
937  return $cache;
938  }
939  }
940 
941  // Render content
942  try {
943  $content .= $contentObject->render($configuration);
944  } catch (ContentRenderingException $exception) {
945  // Content rendering Exceptions indicate a critical problem which should not be
946  // caught e.g. when something went wrong with Exception handling itself
947  throw $exception;
948  } catch (\Exception $exception) {
949  $exceptionHandler = $this->createExceptionHandler($configuration);
950  if ($exceptionHandler === null) {
951  throw $exception;
952  } else {
953  $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
954  }
955  }
956 
957  // Store cache
958  if ($cacheConfiguration !== null) {
959  $key = $this->calculateCacheKey($cacheConfiguration);
960  if (!empty($key)) {
962  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
963  $tags = $this->calculateCacheTags($cacheConfiguration);
964  $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
965  $cacheFrontend->set($key, $content, $tags, $lifetime);
966  }
967  }
968 
969  return $content;
970  }
971 
980  protected function createExceptionHandler($configuration = [])
981  {
982  $exceptionHandler = null;
983  $exceptionHandlerClassName = $this->determineExceptionHandlerClassName($configuration);
984  if (!empty($exceptionHandlerClassName)) {
985  $exceptionHandler = GeneralUtility::makeInstance($exceptionHandlerClassName, $this->mergeExceptionHandlerConfiguration($configuration));
986  if (!$exceptionHandler instanceof ExceptionHandlerInterface) {
987  throw new ContentRenderingException('An exception handler was configured but the class does not exist or does not implement the ExceptionHandlerInterface', 1403653369);
988  }
989  }
990 
991  return $exceptionHandler;
992  }
993 
1000  protected function determineExceptionHandlerClassName($configuration)
1001  {
1002  $exceptionHandlerClassName = null;
1003  $tsfe = $this->getTypoScriptFrontendController();
1004  if (!isset($tsfe->config['config']['contentObjectExceptionHandler'])) {
1005  if (GeneralUtility::getApplicationContext()->isProduction()) {
1006  $exceptionHandlerClassName = '1';
1007  }
1008  } else {
1009  $exceptionHandlerClassName = $tsfe->config['config']['contentObjectExceptionHandler'];
1010  }
1011 
1012  if (isset($configuration['exceptionHandler'])) {
1013  $exceptionHandlerClassName = $configuration['exceptionHandler'];
1014  }
1015 
1016  if ($exceptionHandlerClassName === '1') {
1017  $exceptionHandlerClassName = ProductionExceptionHandler::class;
1018  }
1019 
1020  return $exceptionHandlerClassName;
1021  }
1022 
1030  protected function mergeExceptionHandlerConfiguration($configuration)
1031  {
1032  $exceptionHandlerConfiguration = [];
1033  $tsfe = $this->getTypoScriptFrontendController();
1034  if (!empty($tsfe->config['config']['contentObjectExceptionHandler.'])) {
1035  $exceptionHandlerConfiguration = $tsfe->config['config']['contentObjectExceptionHandler.'];
1036  }
1037  if (!empty($configuration['exceptionHandler.'])) {
1038  $exceptionHandlerConfiguration = array_replace_recursive($exceptionHandlerConfiguration, $configuration['exceptionHandler.']);
1039  }
1040 
1041  return $exceptionHandlerConfiguration;
1042  }
1050  public function FLOWPLAYER($conf)
1051  {
1053  return $this->render($this->getContentObject('FLOWPLAYER'), $conf);
1054  }
1055 
1063  public function TEXT($conf)
1064  {
1066  return $this->render($this->getContentObject('TEXT'), $conf);
1067  }
1068 
1076  public function CLEARGIF($conf)
1077  {
1079  return $this->render($this->getContentObject('CLEARGIF'), $conf);
1080  }
1081 
1090  public function COBJ_ARRAY($conf, $ext = '')
1091  {
1093  if ($ext === 'INT') {
1094  return $this->render($this->getContentObject('COA_INT'), $conf);
1095  } else {
1096  return $this->render($this->getContentObject('COA'), $conf);
1097  }
1098  }
1099 
1108  public function USER($conf, $ext = '')
1109  {
1111  if ($ext === 'INT') {
1112  return $this->render($this->getContentObject('USER_INT'), $conf);
1113  } else {
1114  return $this->render($this->getContentObject('USER'), $conf);
1115  }
1116  }
1117 
1126  public function getUserObjectType()
1127  {
1128  return $this->userObjectType;
1129  }
1130 
1138  {
1139  $this->userObjectType = $userObjectType;
1140  }
1141 
1147  public function convertToUserIntObject()
1148  {
1149  if ($this->userObjectType !== self::OBJECTTYPE_USER) {
1150  $this->getTimeTracker()->setTSlogMessage(self::class . '::convertToUserIntObject() is called in the wrong context or for the wrong object type', 2);
1151  } else {
1152  $this->doConvertToUserIntObject = true;
1153  }
1154  }
1155 
1163  public function FILE($conf)
1164  {
1166  return $this->render($this->getContentObject('FILE'), $conf);
1167  }
1168 
1176  public function FILES($conf)
1177  {
1179  return $this->render($this->getContentObject('FILES'), $conf);
1180  }
1181 
1190  public function IMAGE($conf)
1191  {
1193  return $this->render($this->getContentObject('IMAGE'), $conf);
1194  }
1195 
1204  public function IMG_RESOURCE($conf)
1205  {
1207  return $this->render($this->getContentObject('IMG_RESOURCE'), $conf);
1208  }
1209 
1217  public function IMGTEXT($conf)
1218  {
1220  return $this->render($this->getContentObject('IMGTEXT'), $conf);
1221  }
1222 
1230  public function CONTENT($conf)
1231  {
1233  return $this->render($this->getContentObject('CONTENT'), $conf);
1234  }
1235 
1243  public function RECORDS($conf)
1244  {
1246  return $this->render($this->getContentObject('RECORDS'), $conf);
1247  }
1248 
1256  public function HMENU($conf)
1257  {
1259  return $this->render($this->getContentObject('HMENU'), $conf);
1260  }
1261 
1269  public function CTABLE($conf)
1270  {
1272  return $this->render($this->getContentObject('CTABLE'), $conf);
1273  }
1274 
1282  public function OTABLE($conf)
1283  {
1285  return $this->render($this->getContentObject('OTABLE'), $conf);
1286  }
1287 
1295  public function COLUMNS($conf)
1296  {
1298  return $this->render($this->getContentObject('COLUMNS'), $conf);
1299  }
1300 
1308  public function HRULER($conf)
1309  {
1311  return $this->render($this->getContentObject('HRULER'), $conf);
1312  }
1313 
1321  public function CASEFUNC($conf)
1322  {
1324  return $this->render($this->getContentObject('CASE'), $conf);
1325  }
1326 
1336  public function LOAD_REGISTER($conf, $name)
1337  {
1339  if ($name === 'RESTORE_REGISTER') {
1340  return $this->render($this->getContentObject('RESTORE_REGISTER'), $conf);
1341  } else {
1342  return $this->render($this->getContentObject('LOAD_REGISTER'), $conf);
1343  }
1344  }
1345 
1356  public function FORM($conf, $formData = '')
1357  {
1359  return $this->render($this->getContentObject('FORM'), $conf);
1360  }
1361 
1369  public function SEARCHRESULT($conf)
1370  {
1372  return $this->render($this->getContentObject('SEARCHRESULT'), $conf);
1373  }
1374 
1383  public function TEMPLATE($conf)
1384  {
1386  return $this->render($this->getContentObject('TEMPLATE'), $conf);
1387  }
1388 
1396  protected function FLUIDTEMPLATE(array $conf)
1397  {
1399  return $this->render($this->getContentObject('FLUIDTEMPLATE'), $conf);
1400  }
1401 
1409  public function MULTIMEDIA($conf)
1410  {
1412  return $this->render($this->getContentObject('MULTIMEDIA'), $conf);
1413  }
1414 
1422  public function MEDIA($conf)
1423  {
1425  return $this->render($this->getContentObject('MEDIA'), $conf);
1426  }
1427 
1435  public function SWFOBJECT($conf)
1436  {
1438  return $this->render($this->getContentObject('SWFOBJECT'), $conf);
1439  }
1440 
1448  public function QTOBJECT($conf)
1449  {
1451  return $this->render($this->getContentObject('QTOBJECT'), $conf);
1452  }
1453 
1461  public function SVG($conf)
1462  {
1464  return $this->render($this->getContentObject('SVG'), $conf);
1465  }
1466 
1467  /************************************
1468  *
1469  * Various helper functions for content objects:
1470  *
1471  ************************************/
1480  public function readFlexformIntoConf($flexData, &$conf, $recursive = false)
1481  {
1482  if ($recursive === false && is_string($flexData)) {
1483  $flexData = GeneralUtility::xml2array($flexData, 'T3');
1484  }
1485  if (is_array($flexData) && isset($flexData['data']['sDEF']['lDEF'])) {
1486  $flexData = $flexData['data']['sDEF']['lDEF'];
1487  }
1488  if (!is_array($flexData)) {
1489  return;
1490  }
1491  foreach ($flexData as $key => $value) {
1492  if (!is_array($value)) {
1493  continue;
1494  }
1495  if (isset($value['el'])) {
1496  if (is_array($value['el']) && !empty($value['el'])) {
1497  foreach ($value['el'] as $ekey => $element) {
1498  if (isset($element['vDEF'])) {
1499  $conf[$ekey] = $element['vDEF'];
1500  } else {
1501  if (is_array($element)) {
1502  $this->readFlexformIntoConf($element, $conf[$key][key($element)][$ekey], true);
1503  } else {
1504  $this->readFlexformIntoConf($element, $conf[$key][$ekey], true);
1505  }
1506  }
1507  }
1508  } else {
1509  $this->readFlexformIntoConf($value['el'], $conf[$key], true);
1510  }
1511  }
1512  if (isset($value['vDEF'])) {
1513  $conf[$key] = $value['vDEF'];
1514  }
1515  }
1516  }
1517 
1526  public function getSlidePids($pidList, $pidConf)
1527  {
1528  $pidList = isset($pidConf) ? trim($this->stdWrap($pidList, $pidConf)) : trim($pidList);
1529  if ($pidList === '') {
1530  $pidList = 'this';
1531  }
1532  $tsfe = $this->getTypoScriptFrontendController();
1533  $listArr = null;
1534  if (trim($pidList)) {
1535  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $pidList));
1536  $listArr = $this->checkPidArray($listArr);
1537  }
1538  $pidList = [];
1539  if (is_array($listArr) && !empty($listArr)) {
1540  foreach ($listArr as $uid) {
1541  $page = $tsfe->sys_page->getPage($uid);
1542  if (!$page['is_siteroot']) {
1543  $pidList[] = $page['pid'];
1544  }
1545  }
1546  }
1547  return implode(',', $pidList);
1548  }
1549 
1561  public function cImage($file, $conf)
1562  {
1563  $tsfe = $this->getTypoScriptFrontendController();
1564  $info = $this->getImgResource($file, $conf['file.']);
1565  $tsfe->lastImageInfo = $info;
1566  if (!is_array($info)) {
1567  return '';
1568  }
1569  if (is_file(PATH_site . $info['3'])) {
1570  $source = $tsfe->absRefPrefix . GeneralUtility::rawUrlEncodeFP($info['3']);
1571  } else {
1572  $source = $info[3];
1573  }
1574 
1575  $layoutKey = $this->stdWrap($conf['layoutKey'], $conf['layoutKey.']);
1576  $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
1577  $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
1578 
1579  // This array is used to collect the image-refs on the page...
1580  $tsfe->imagesOnPage[] = $source;
1581  $altParam = $this->getAltParam($conf);
1582  $params = $this->stdWrapValue('params', $conf);
1583  if ($params !== '' && $params[0] !== ' ') {
1584  $params = ' ' . $params;
1585  }
1586 
1587  $imageTagValues = [
1588  'width' => (int)$info[0],
1589  'height' => (int)$info[1],
1590  'src' => htmlspecialchars($source),
1591  'params' => $params,
1592  'altParams' => $altParam,
1593  'border' => $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
1594  'sourceCollection' => $sourceCollection,
1595  'selfClosingTagSlash' => (!empty($tsfe->xhtmlDoctype) ? ' /' : ''),
1596  ];
1597 
1598  $theValue = $this->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
1599 
1600  $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
1601  if ($linkWrap) {
1602  $theValue = $this->linkWrap($theValue, $linkWrap);
1603  } elseif ($conf['imageLinkWrap']) {
1604  $originalFile = !empty($info['originalFile']) ? $info['originalFile'] : $info['origFile'];
1605  $theValue = $this->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
1606  }
1607  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
1608  if ((string)$wrap !== '') {
1609  $theValue = $this->wrap($theValue, $conf['wrap']);
1610  }
1611  return $theValue;
1612  }
1613 
1621  public function getBorderAttr($borderAttr)
1622  {
1623  $tsfe = $this->getTypoScriptFrontendController();
1624  $docType = $tsfe->xhtmlDoctype;
1625  if (
1626  $docType !== 'xhtml_strict' && $docType !== 'xhtml_11' && $docType !== 'xhtml_2'
1627  && $tsfe->config['config']['doctype'] !== 'html5'
1628  && !$tsfe->config['config']['disableImgBorderAttr']
1629  ) {
1630  return $borderAttr;
1631  }
1632  return '';
1633  }
1634 
1643  public function getImageTagTemplate($layoutKey, $conf)
1644  {
1645  if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
1646  $imageTagLayout = $this->stdWrap($conf['layout.'][$layoutKey . '.']['element'], $conf['layout.'][$layoutKey . '.']['element.']);
1647  } else {
1648  $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1649  }
1650  return $imageTagLayout;
1651  }
1652 
1662  public function getImageSourceCollection($layoutKey, $conf, $file)
1663  {
1664  $sourceCollection = '';
1665  if ($layoutKey && $conf['sourceCollection.'] && ($conf['layout.'][$layoutKey . '.']['source'] || $conf['layout.'][$layoutKey . '.']['source.'])) {
1666 
1667  // find active sourceCollection
1668  $activeSourceCollections = [];
1669  foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
1670  if (substr($sourceCollectionKey, -1) === '.') {
1671  if (empty($sourceCollectionConfiguration['if.']) || $this->checkIf($sourceCollectionConfiguration['if.'])) {
1672  $activeSourceCollections[] = $sourceCollectionConfiguration;
1673  }
1674  }
1675  }
1676 
1677  // apply option split to configurations
1678  $tsfe = $this->getTypoScriptFrontendController();
1679  $srcLayoutOptionSplitted = $tsfe->tmpl->splitConfArray($conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
1680 
1681  // render sources
1682  foreach ($activeSourceCollections as $key => $sourceConfiguration) {
1683  $sourceLayout = $this->stdWrap($srcLayoutOptionSplitted[$key]['source'], $srcLayoutOptionSplitted[$key]['source.']);
1684 
1685  $sourceRenderConfiguration = [
1686  'file' => $file,
1687  'file.' => $conf['file.']
1688  ];
1689 
1690  if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
1691  $imageQuality = isset($sourceConfiguration['quality']) ? $sourceConfiguration['quality'] : '';
1692  if (isset($sourceConfiguration['quality.'])) {
1693  $imageQuality = $this->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
1694  }
1695  if ($imageQuality) {
1696  $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
1697  }
1698  }
1699 
1700  if (isset($sourceConfiguration['pixelDensity'])) {
1701  $pixelDensity = (int)$this->stdWrap($sourceConfiguration['pixelDensity'], $sourceConfiguration['pixelDensity.']);
1702  } else {
1703  $pixelDensity = 1;
1704  }
1705  $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH'];
1706  foreach ($dimensionKeys as $dimensionKey) {
1707  $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
1708  if (!$dimension) {
1709  $dimension = $this->stdWrap($conf['file.'][$dimensionKey], $conf['file.'][$dimensionKey . '.']);
1710  }
1711  if ($dimension) {
1712  if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
1713  $dimensionParts = explode('c', $dimension, 2);
1714  $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
1715  if ($dimensionParts[1]) {
1716  $dimension .= $dimensionParts[1];
1717  }
1718  } else {
1719  $dimension = (int)$dimension * $pixelDensity;
1720  }
1721  $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
1722  // Remove the stdWrap properties for dimension as they have been processed already above.
1723  unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
1724  }
1725  }
1726  $sourceInfo = $this->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
1727  if ($sourceInfo) {
1728  $sourceConfiguration['width'] = $sourceInfo[0];
1729  $sourceConfiguration['height'] = $sourceInfo[1];
1730  $urlPrefix = '';
1731  if (parse_url($sourceInfo[3], PHP_URL_HOST) === null) {
1732  $urlPrefix = $tsfe->absRefPrefix;
1733  }
1734  $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
1735  $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
1736 
1737  $oneSourceCollection = $this->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
1738 
1739  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'])) {
1740  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] as $classData) {
1741  $hookObject = GeneralUtility::getUserObj($classData);
1742  if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
1743  throw new \UnexpectedValueException(
1744  '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
1745  1380007853
1746  );
1747  }
1748  $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this);
1749  }
1750  }
1751 
1752  $sourceCollection .= $oneSourceCollection;
1753  }
1754  }
1755  }
1756  return $sourceCollection;
1757  }
1758 
1768  public function imageLinkWrap($string, $imageFile, $conf)
1769  {
1770  $string = (string)$string;
1771  $enable = isset($conf['enable.']) ? $this->stdWrap($conf['enable'], $conf['enable.']) : $conf['enable'];
1772  if (!$enable) {
1773  return $string;
1774  }
1775  $content = (string)$this->typoLink($string, $conf['typolink.']);
1776  if (isset($conf['file.'])) {
1777  $imageFile = $this->stdWrap($imageFile, $conf['file.']);
1778  }
1779 
1780  if ($imageFile instanceof File) {
1781  $file = $imageFile;
1782  } elseif ($imageFile instanceof FileReference) {
1783  $file = $imageFile->getOriginalFile();
1784  } else {
1785  if (MathUtility::canBeInterpretedAsInteger($imageFile)) {
1786  $file = ResourceFactory::getInstance()->getFileObject((int)$imageFile);
1787  } else {
1788  $file = ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($imageFile);
1789  }
1790  }
1791 
1792  // Create imageFileLink if not created with typolink
1793  if ($content === $string) {
1794  $parameterNames = ['width', 'height', 'effects', 'bodyTag', 'title', 'wrap', 'crop'];
1795  $parameters = [];
1796  $sample = isset($conf['sample.']) ? $this->stdWrap($conf['sample'], $conf['sample.']) : $conf['sample'];
1797  if ($sample) {
1798  $parameters['sample'] = 1;
1799  }
1800  foreach ($parameterNames as $parameterName) {
1801  if (isset($conf[$parameterName . '.'])) {
1802  $conf[$parameterName] = $this->stdWrap($conf[$parameterName], $conf[$parameterName . '.']);
1803  }
1804  if (isset($conf[$parameterName]) && $conf[$parameterName]) {
1805  $parameters[$parameterName] = $conf[$parameterName];
1806  }
1807  }
1808  $parametersEncoded = base64_encode(serialize($parameters));
1809  $hmac = GeneralUtility::hmac(implode('|', [$file->getUid(), $parametersEncoded]));
1810  $params = '&md5=' . $hmac;
1811  foreach (str_split($parametersEncoded, 64) as $index => $chunk) {
1812  $params .= '&parameters' . rawurlencode('[') . $index . rawurlencode(']') . '=' . rawurlencode($chunk);
1813  }
1814  $url = $this->getTypoScriptFrontendController()->absRefPrefix . 'index.php?eID=tx_cms_showpic&file=' . $file->getUid() . $params;
1815  $directImageLink = isset($conf['directImageLink.']) ? $this->stdWrap($conf['directImageLink'], $conf['directImageLink.']) : $conf['directImageLink'];
1816  if ($directImageLink) {
1817  $imgResourceConf = [
1818  'file' => $imageFile,
1819  'file.' => $conf
1820  ];
1821  $url = $this->cObjGetSingle('IMG_RESOURCE', $imgResourceConf);
1822  if (!$url) {
1823  // If no imagemagick / gm is available
1824  $url = $imageFile;
1825  }
1826  }
1827  // Create TARGET-attribute only if the right doctype is used
1828  $target = '';
1829  $xhtmlDocType = $this->getTypoScriptFrontendController()->xhtmlDoctype;
1830  if ($xhtmlDocType !== 'xhtml_strict' && $xhtmlDocType !== 'xhtml_11' && $xhtmlDocType !== 'xhtml_2') {
1831  $target = isset($conf['target.'])
1832  ? (string)$this->stdWrap($conf['target'], $conf['target.'])
1833  : (string)$conf['target'];
1834  if ($target === '') {
1835  $target = 'thePicture';
1836  }
1837  }
1838  $a1 = '';
1839  $a2 = '';
1840  $conf['JSwindow'] = isset($conf['JSwindow.']) ? $this->stdWrap($conf['JSwindow'], $conf['JSwindow.']) : $conf['JSwindow'];
1841  if ($conf['JSwindow']) {
1842  if ($conf['JSwindow.']['altUrl'] || $conf['JSwindow.']['altUrl.']) {
1843  $altUrl = isset($conf['JSwindow.']['altUrl.']) ? $this->stdWrap($conf['JSwindow.']['altUrl'], $conf['JSwindow.']['altUrl.']) : $conf['JSwindow.']['altUrl'];
1844  if ($altUrl) {
1845  $url = $altUrl . ($conf['JSwindow.']['altUrl_noDefaultParams'] ? '' : '?file=' . rawurlencode($imageFile) . $params);
1846  }
1847  }
1848 
1849  $processedFile = $file->process('Image.CropScaleMask', $conf);
1850  $JSwindowExpand = isset($conf['JSwindow.']['expand.']) ? $this->stdWrap($conf['JSwindow.']['expand'], $conf['JSwindow.']['expand.']) : $conf['JSwindow.']['expand'];
1851  $offset = GeneralUtility::intExplode(',', $JSwindowExpand . ',');
1852  $newWindow = isset($conf['JSwindow.']['newWindow.']) ? $this->stdWrap($conf['JSwindow.']['newWindow'], $conf['JSwindow.']['newWindow.']) : $conf['JSwindow.']['newWindow'];
1853  $onClick = 'openPic('
1854  . GeneralUtility::quoteJSvalue($this->getTypoScriptFrontendController()->baseUrlWrap($url)) . ','
1855  . '\'' . ($newWindow ? md5($url) : 'thePicture') . '\','
1856  . GeneralUtility::quoteJSvalue('width=' . ($processedFile->getProperty('width') + $offset[0])
1857  . ',height=' . ($processedFile->getProperty('height') + $offset[1]) . ',status=0,menubar=0')
1858  . '); return false;';
1859  $a1 = '<a href="' . htmlspecialchars($url) . '"'
1860  . ' onclick="' . htmlspecialchars($onClick) . '"'
1861  . ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
1862  . $this->getTypoScriptFrontendController()->ATagParams . '>';
1863  $a2 = '</a>';
1864  $this->getTypoScriptFrontendController()->setJS('openPic');
1865  } else {
1866  $conf['linkParams.']['parameter'] = $url;
1867  $string = $this->typoLink($string, $conf['linkParams.']);
1868  }
1869  if (isset($conf['stdWrap.'])) {
1870  $string = $this->stdWrap($string, $conf['stdWrap.']);
1871  }
1872  $content = $a1 . $string . $a2;
1873  }
1874  return $content;
1875  }
1876 
1885  public function fileResource($fName, $addParams = 'alt="" title=""')
1886  {
1887  $tsfe = $this->getTypoScriptFrontendController();
1888  $incFile = $tsfe->tmpl->getFileName($fName);
1889  if ($incFile && file_exists($incFile)) {
1890  $fileInfo = GeneralUtility::split_fileref($incFile);
1891  $extension = $fileInfo['fileext'];
1892  if ($extension === 'jpg' || $extension === 'jpeg' || $extension === 'gif' || $extension === 'png') {
1893  $imgFile = $incFile;
1894  $imgInfo = @getimagesize($imgFile);
1895  return '<img src="' . htmlspecialchars($tsfe->absRefPrefix . $imgFile) . '" width="' . (int)$imgInfo[0] . '" height="' . (int)$imgInfo[1] . '"' . $this->getBorderAttr(' border="0"') . ' ' . $addParams . ' />';
1896  } elseif (filesize($incFile) < 1024 * 1024) {
1897  return $tsfe->tmpl->fileContent($incFile);
1898  }
1899  }
1900  return '';
1901  }
1902 
1912  public function lastChanged($tstamp)
1913  {
1914  $tstamp = (int)$tstamp;
1915  $tsfe = $this->getTypoScriptFrontendController();
1916  if ($tstamp > (int)$tsfe->register['SYS_LASTCHANGED']) {
1917  $tsfe->register['SYS_LASTCHANGED'] = $tstamp;
1918  }
1919  }
1920 
1930  public function linkWrap($content, $wrap)
1931  {
1932  $wrapArr = explode('|', $wrap);
1933  if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
1934  if ($uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid']) {
1935  $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
1936  }
1937  }
1938  return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
1939  }
1940 
1950  public function getAltParam($conf, $longDesc = true)
1951  {
1952  $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
1953  $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
1954  if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] != 'html5') {
1955  $longDescUrl = $this->typoLink_URL($conf['longdescURL.']);
1956  } else {
1957  $longDescUrl = trim($conf['longdescURL']);
1958  }
1959  $longDescUrl = strip_tags($longDescUrl);
1960 
1961  // "alt":
1962  $altParam = ' alt="' . htmlspecialchars($altText) . '"';
1963  // "title":
1964  $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
1965  // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
1966  if ($titleText || $emptyTitleHandling === 'keepEmpty') {
1967  $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
1968  } elseif (!$titleText && $emptyTitleHandling === 'useAlt') {
1969  $altParam .= ' title="' . htmlspecialchars($altText) . '"';
1970  }
1971  // "longDesc" URL
1972  if ($longDesc && !empty($longDescUrl)) {
1973  $altParam .= ' longdesc="' . htmlspecialchars($longDescUrl) . '"';
1974  }
1975  return $altParam;
1976  }
1977 
1986  public function cleanFormName($name)
1987  {
1988  GeneralUtility::logDeprecatedFunction();
1989  // Turn data[x][y] into data:x:y:
1990  $name = preg_replace('/\\[|\\]\\[?/', ':', trim($name));
1991  // Remove illegal chars like _
1992  return preg_replace('#[^:a-zA-Z0-9]#', '', $name);
1993  }
1994 
2004  public function getATagParams($conf, $addGlobal = 1)
2005  {
2006  $aTagParams = '';
2007  if ($conf['ATagParams.']) {
2008  $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
2009  } elseif ($conf['ATagParams']) {
2010  $aTagParams = ' ' . $conf['ATagParams'];
2011  }
2012  if ($addGlobal) {
2013  $aTagParams = ' ' . trim($this->getTypoScriptFrontendController()->ATagParams . $aTagParams);
2014  }
2015  // Extend params
2016  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'])) {
2017  $_params = [
2018  'conf' => &$conf,
2019  'aTagParams' => &$aTagParams
2020  ];
2021  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] as $objRef) {
2022  $processor =& GeneralUtility::getUserObj($objRef);
2023  $aTagParams = $processor->process($_params, $this);
2024  }
2025  }
2026 
2027  $aTagParams = trim($aTagParams);
2028  if (!empty($aTagParams)) {
2029  $aTagParams = ' ' . $aTagParams;
2030  }
2031 
2032  return $aTagParams;
2033  }
2034 
2043  public function extLinkATagParams($URL, $TYPE)
2044  {
2045  $out = '';
2046  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
2047  $extLinkATagParamsHandler = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
2048  if (method_exists($extLinkATagParamsHandler, 'main')) {
2049  $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
2050  }
2051  }
2052  return trim($out) ? ' ' . trim($out) : '';
2053  }
2054 
2055  /***********************************************
2056  *
2057  * HTML template processing functions
2058  *
2059  ***********************************************/
2078  public function getSubpart($content, $marker)
2079  {
2080  return $this->templateService->getSubpart($content, $marker);
2081  }
2082 
2095  public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1)
2096  {
2097  return $this->templateService->substituteSubpart($content, $marker, $subpartContent, $recursive);
2098  }
2099 
2107  public function substituteSubpartArray($content, array $subpartsContent)
2108  {
2109  return $this->templateService->substituteSubpartArray($content, $subpartsContent);
2110  }
2111 
2122  public function substituteMarker($content, $marker, $markContent)
2123  {
2124  return $this->templateService->substituteMarker($content, $marker, $markContent);
2125  }
2126 
2159  public function substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
2160  {
2161  $timeTracker = $this->getTimeTracker();
2162  $timeTracker->push('substituteMarkerArrayCached');
2163  // If not arrays then set them
2164  if (is_null($markContentArray)) {
2165  // Plain markers
2166  $markContentArray = [];
2167  }
2168  if (is_null($subpartContentArray)) {
2169  // Subparts being directly substituted
2170  $subpartContentArray = [];
2171  }
2172  if (is_null($wrappedSubpartContentArray)) {
2173  // Subparts being wrapped
2174  $wrappedSubpartContentArray = [];
2175  }
2176  // Finding keys and check hash:
2177  $sPkeys = array_keys($subpartContentArray);
2178  $wPkeys = array_keys($wrappedSubpartContentArray);
2179  $keysToReplace = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
2180  if (empty($keysToReplace)) {
2181  $timeTracker->pull();
2182  return $content;
2183  }
2184  asort($keysToReplace);
2185  $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize([$content, $keysToReplace]));
2186  if ($this->substMarkerCache[$storeKey]) {
2187  $storeArr = $this->substMarkerCache[$storeKey];
2188  $timeTracker->setTSlogMessage('Cached', 0);
2189  } else {
2190  $storeArrDat = $this->getTypoScriptFrontendController()->sys_page->getHash($storeKey);
2191  if (is_array($storeArrDat)) {
2192  $storeArr = $storeArrDat;
2193  // Setting cache:
2194  $this->substMarkerCache[$storeKey] = $storeArr;
2195  $timeTracker->setTSlogMessage('Cached from DB', 0);
2196  } else {
2197  // Finding subparts and substituting them with the subpart as a marker
2198  foreach ($sPkeys as $sPK) {
2199  $content = $this->substituteSubpart($content, $sPK, $sPK);
2200  }
2201  // Finding subparts and wrapping them with markers
2202  foreach ($wPkeys as $wPK) {
2203  $content = $this->substituteSubpart($content, $wPK, [
2204  $wPK,
2205  $wPK
2206  ]);
2207  }
2208 
2209  $storeArr = [];
2210  // search all markers in the content
2211  $result = preg_match_all('/###([^#](?:[^#]*+|#{1,2}[^#])+)###/', $content, $markersInContent);
2212  if ($result !== false && !empty($markersInContent[1])) {
2213  $keysToReplaceFlipped = array_flip($keysToReplace);
2214  $regexKeys = [];
2215  $wrappedKeys = [];
2216  // Traverse keys and quote them for reg ex.
2217  foreach ($markersInContent[1] as $key) {
2218  if (isset($keysToReplaceFlipped['###' . $key . '###'])) {
2219  $regexKeys[] = preg_quote($key, '/');
2220  $wrappedKeys[] = '###' . $key . '###';
2221  }
2222  }
2223  $regex = '/###(?:' . implode('|', $regexKeys) . ')###/';
2224  $storeArr['c'] = preg_split($regex, $content); // contains all content parts around markers
2225  $storeArr['k'] = $wrappedKeys; // contains all markers incl. ###
2226  // Setting cache:
2227  $this->substMarkerCache[$storeKey] = $storeArr;
2228  // Storing the cached data:
2229  $this->getTypoScriptFrontendController()->sys_page->storeHash($storeKey, $storeArr, 'substMarkArrayCached');
2230  }
2231  $timeTracker->setTSlogMessage('Parsing', 0);
2232  }
2233  }
2234  if (!empty($storeArr['k']) && is_array($storeArr['k'])) {
2235  // Substitution/Merging:
2236  // Merging content types together, resetting
2237  $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
2238  $wSCA_reg = [];
2239  $content = '';
2240  // Traversing the keyList array and merging the static and dynamic content
2241  foreach ($storeArr['k'] as $n => $keyN) {
2242  // add content before marker
2243  $content .= $storeArr['c'][$n];
2244  if (!is_array($valueArr[$keyN])) {
2245  // fetch marker replacement from $markContentArray or $subpartContentArray
2246  $content .= $valueArr[$keyN];
2247  } else {
2248  if (!isset($wSCA_reg[$keyN])) {
2249  $wSCA_reg[$keyN] = 0;
2250  }
2251  // fetch marker replacement from $wrappedSubpartContentArray
2252  $content .= $valueArr[$keyN][$wSCA_reg[$keyN] % 2];
2253  $wSCA_reg[$keyN]++;
2254  }
2255  }
2256  // add remaining content
2257  $content .= $storeArr['c'][count($storeArr['k'])];
2258  }
2259  $timeTracker->pull();
2260  return $content;
2261  }
2262 
2282  public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
2283  {
2284  return $this->templateService->substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
2285  }
2286 
2295  public function substituteMarkerInObject(&$tree, array $markContentArray)
2296  {
2297  if (is_array($tree)) {
2298  foreach ($tree as $key => $value) {
2299  $this->substituteMarkerInObject($tree[$key], $markContentArray);
2300  }
2301  } else {
2302  $tree = $this->substituteMarkerArray($tree, $markContentArray);
2303  }
2304  return $tree;
2305  }
2306 
2317  public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
2318  {
2319  return $this->templateService->substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
2320  }
2321 
2334  public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $HSC = false)
2335  {
2336  $tsfe = $this->getTypoScriptFrontendController();
2337  if ($fieldList) {
2338  $fArr = GeneralUtility::trimExplode(',', $fieldList, true);
2339  foreach ($fArr as $field) {
2340  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], !empty($tsfe->xhtmlDoctype)) : $row[$field];
2341  }
2342  } else {
2343  if (is_array($row)) {
2344  foreach ($row as $field => $value) {
2345  if (!MathUtility::canBeInterpretedAsInteger($field)) {
2346  if ($HSC) {
2347  $value = htmlspecialchars($value);
2348  }
2349  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, !empty($tsfe->xhtmlDoctype)) : $value;
2350  }
2351  }
2352  }
2353  }
2354  return $markContentArray;
2355  }
2356 
2362  public function setCurrentFile($fileObject)
2363  {
2364  $this->currentFile = $fileObject;
2365  }
2366 
2372  public function getCurrentFile()
2373  {
2374  return $this->currentFile;
2375  }
2376 
2377  /***********************************************
2378  *
2379  * "stdWrap" + sub functions
2380  *
2381  ***********************************************/
2393  public function stdWrap($content = '', $conf = [])
2394  {
2395  $content = (string)$content;
2396  // If there is any hook object, activate all of the process and override functions.
2397  // The hook interface ContentObjectStdWrapHookInterface takes care that all 4 methods exist.
2398  if ($this->stdWrapHookObjects) {
2399  $conf['stdWrapPreProcess'] = 1;
2400  $conf['stdWrapOverride'] = 1;
2401  $conf['stdWrapProcess'] = 1;
2402  $conf['stdWrapPostProcess'] = 1;
2403  }
2404 
2405  if (!is_array($conf) || !$conf) {
2406  return $content;
2407  }
2408 
2409  // Cache handling
2410  if (is_array($conf['cache.'])) {
2411  $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
2412  $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
2413  $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
2414  $conf['cacheRead'] = 1;
2415  $conf['cacheStore'] = 1;
2416  }
2417  // Check, which of the available stdWrap functions is needed for the current conf Array
2418  // and keep only those but still in the same order
2419  $sortedConf = array_intersect_key($this->stdWrapOrder, $conf);
2420  // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
2421  $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
2422  // Additional Array to check whether a function has already been executed
2423  $isExecuted = [];
2424  // Additional switch to make sure 'required', 'if' and 'fieldRequired'
2425  // will still stop rendering immediately in case they return FALSE
2426  $this->stdWrapRecursionLevel++;
2427  $this->stopRendering[$this->stdWrapRecursionLevel] = false;
2428  // execute each function in the predefined order
2429  foreach ($sortedConf as $stdWrapName => $functionType) {
2430  // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
2431  if (!$isExecuted[$stdWrapName] && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
2432  $functionName = rtrim($stdWrapName, '.');
2433  $functionProperties = $functionName . '.';
2434  // If there is any code on the next level, check if it contains "official" stdWrap functions
2435  // if yes, execute them first - will make each function stdWrap aware
2436  // so additional stdWrap calls within the functions can be removed, since the result will be the same
2437  // exception: the recursive stdWrap function and cObject will still be using their own stdWrap call, since it modifies the content and not a property
2438  if ($functionName !== 'stdWrap' && !empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
2439  if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
2440  $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
2441  }
2442  }
2443  // Check if key is still containing something, since it might have been changed by next level stdWrap before
2444  if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
2445  // Get just that part of $conf that is needed for the particular function
2446  $singleConf = [
2447  $functionName => $conf[$functionName],
2448  $functionProperties => $conf[$functionProperties]
2449  ];
2450  // In this special case 'spaceBefore' and 'spaceAfter' need additional stuff from 'space.''
2451  if ($functionName === 'spaceBefore' || $functionName === 'spaceAfter') {
2452  $singleConf['space.'] = $conf['space.'];
2453  }
2454  // Hand over the whole $conf array to the stdWrapHookObjects
2455  if ($functionType === 'hook') {
2456  $singleConf = $conf;
2457  }
2458  // Add both keys - with and without the dot - to the set of executed functions
2459  $isExecuted[$functionName] = true;
2460  $isExecuted[$functionProperties] = true;
2461  // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
2462  $functionName = 'stdWrap_' . $functionName;
2463  $content = $this->{$functionName}($content, $singleConf);
2464  } elseif ($functionType === 'boolean' && !$conf[$functionName]) {
2465  $isExecuted[$functionName] = true;
2466  $isExecuted[$functionProperties] = true;
2467  }
2468  }
2469  }
2470  unset($this->stopRendering[$this->stdWrapRecursionLevel]);
2471  $this->stdWrapRecursionLevel--;
2472 
2473  return $content;
2474  }
2475 
2484  public function stdWrapValue($key, array $config, $defaultValue = '')
2485  {
2486  if (isset($config[$key])) {
2487  if (!isset($config[$key . '.'])) {
2488  return $config[$key];
2489  }
2490  } elseif (isset($config[$key . '.'])) {
2491  $config[$key] = '';
2492  } else {
2493  return $defaultValue;
2494  }
2495  $stdWrapped = $this->stdWrap($config[$key], $config[$key . '.']);
2496  return $stdWrapped ?: $defaultValue;
2497  }
2498 
2508  public function stdWrap_stdWrapPreProcess($content = '', $conf = [])
2509  {
2510  foreach ($this->stdWrapHookObjects as $hookObject) {
2512  $content = $hookObject->stdWrapPreProcess($content, $conf, $this);
2513  }
2514  return $content;
2515  }
2516 
2524  public function stdWrap_cacheRead($content = '', $conf = [])
2525  {
2526  if (!isset($conf['cache.'])) {
2527  return $content;
2528  }
2529  $result = $this->getFromCache($conf['cache.']);
2530  return $result === false ? $content : $result;
2531  }
2532 
2540  public function stdWrap_addPageCacheTags($content = '', $conf = [])
2541  {
2542  $tags = isset($conf['addPageCacheTags.'])
2543  ? $this->stdWrap($conf['addPageCacheTags'], $conf['addPageCacheTags.'])
2544  : $conf['addPageCacheTags'];
2545  if (!empty($tags)) {
2546  $cacheTags = GeneralUtility::trimExplode(',', $tags, true);
2547  $this->getTypoScriptFrontendController()->addCacheTags($cacheTags);
2548  }
2549  return $content;
2550  }
2551 
2559  public function stdWrap_setContentToCurrent($content = '')
2560  {
2561  $this->data[$this->currentValKey] = $content;
2562  return $content;
2563  }
2564 
2573  public function stdWrap_setCurrent($content = '', $conf = [])
2574  {
2575  $this->data[$this->currentValKey] = $conf['setCurrent'];
2576  return $content;
2577  }
2578 
2587  public function stdWrap_lang($content = '', $conf = [])
2588  {
2589  $tsfe = $this->getTypoScriptFrontendController();
2590  if (isset($conf['lang.']) && $tsfe->config['config']['language'] && isset($conf['lang.'][$tsfe->config['config']['language']])) {
2591  $content = $conf['lang.'][$tsfe->config['config']['language']];
2592  }
2593  return $content;
2594  }
2595 
2604  public function stdWrap_data($content = '', $conf = [])
2605  {
2606  $content = $this->getData($conf['data'], is_array($this->alternativeData) ? $this->alternativeData : $this->data);
2607  // This must be unset directly after
2608  $this->alternativeData = '';
2609  return $content;
2610  }
2611 
2620  public function stdWrap_field($content = '', $conf = [])
2621  {
2622  return $this->getFieldVal($conf['field']);
2623  }
2624 
2634  public function stdWrap_current($content = '', $conf = [])
2635  {
2636  return $this->data[$this->currentValKey];
2637  }
2638 
2648  public function stdWrap_cObject($content = '', $conf = [])
2649  {
2650  return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
2651  }
2652 
2662  public function stdWrap_numRows($content = '', $conf = [])
2663  {
2664  return $this->numRows($conf['numRows.']);
2665  }
2666 
2675  public function stdWrap_filelist($content = '', $conf = [])
2676  {
2677  return $this->filelist($conf['filelist']);
2678  }
2679 
2688  public function stdWrap_preUserFunc($content = '', $conf = [])
2689  {
2690  return $this->callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'], $content);
2691  }
2692 
2702  public function stdWrap_stdWrapOverride($content = '', $conf = [])
2703  {
2704  foreach ($this->stdWrapHookObjects as $hookObject) {
2706  $content = $hookObject->stdWrapOverride($content, $conf, $this);
2707  }
2708  return $content;
2709  }
2710 
2719  public function stdWrap_override($content = '', $conf = [])
2720  {
2721  if (trim($conf['override'])) {
2722  $content = $conf['override'];
2723  }
2724  return $content;
2725  }
2726 
2736  public function stdWrap_preIfEmptyListNum($content = '', $conf = [])
2737  {
2738  return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
2739  }
2740 
2749  public function stdWrap_ifNull($content = '', $conf = [])
2750  {
2751  return $content !== null ? $content : $conf['ifNull'];
2752  }
2753 
2763  public function stdWrap_ifEmpty($content = '', $conf = [])
2764  {
2765  if (!trim($content)) {
2766  $content = $conf['ifEmpty'];
2767  }
2768  return $content;
2769  }
2770 
2780  public function stdWrap_ifBlank($content = '', $conf = [])
2781  {
2782  if (trim($content) === '') {
2783  $content = $conf['ifBlank'];
2784  }
2785  return $content;
2786  }
2787 
2798  public function stdWrap_listNum($content = '', $conf = [])
2799  {
2800  return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
2801  }
2802 
2810  public function stdWrap_trim($content = '')
2811  {
2812  return trim($content);
2813  }
2814 
2823  public function stdWrap_strPad($content = '', $conf = [])
2824  {
2825  // Must specify a length in conf for this to make sense
2826  $length = 0;
2827  // Padding with space is PHP-default
2828  $padWith = ' ';
2829  // Padding on the right side is PHP-default
2830  $padType = STR_PAD_RIGHT;
2831  if (!empty($conf['strPad.']['length'])) {
2832  $length = isset($conf['strPad.']['length.']) ? $this->stdWrap($conf['strPad.']['length'], $conf['strPad.']['length.']) : $conf['strPad.']['length'];
2833  $length = (int)$length;
2834  }
2835  if (isset($conf['strPad.']['padWith']) && (string)$conf['strPad.']['padWith'] !== '') {
2836  $padWith = isset($conf['strPad.']['padWith.']) ? $this->stdWrap($conf['strPad.']['padWith'], $conf['strPad.']['padWith.']) : $conf['strPad.']['padWith'];
2837  }
2838  if (!empty($conf['strPad.']['type'])) {
2839  $type = isset($conf['strPad.']['type.']) ? $this->stdWrap($conf['strPad.']['type'], $conf['strPad.']['type.']) : $conf['strPad.']['type'];
2840  if (strtolower($type) === 'left') {
2841  $padType = STR_PAD_LEFT;
2842  } elseif (strtolower($type) === 'both') {
2843  $padType = STR_PAD_BOTH;
2844  }
2845  }
2846  return str_pad($content, $length, $padWith, $padType);
2847  }
2848 
2860  public function stdWrap_stdWrap($content = '', $conf = [])
2861  {
2862  return $this->stdWrap($content, $conf['stdWrap.']);
2863  }
2864 
2874  public function stdWrap_stdWrapProcess($content = '', $conf = [])
2875  {
2876  foreach ($this->stdWrapHookObjects as $hookObject) {
2878  $content = $hookObject->stdWrapProcess($content, $conf, $this);
2879  }
2880  return $content;
2881  }
2882 
2891  public function stdWrap_required($content = '')
2892  {
2893  if ((string)$content === '') {
2894  $content = '';
2895  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2896  }
2897  return $content;
2898  }
2899 
2909  public function stdWrap_if($content = '', $conf = [])
2910  {
2911  if (empty($conf['if.']) || $this->checkIf($conf['if.'])) {
2912  return $content;
2913  }
2914  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2915  return '';
2916  }
2917 
2927  public function stdWrap_fieldRequired($content = '', $conf = [])
2928  {
2929  if (!trim($this->data[$conf['fieldRequired']])) {
2930  $content = '';
2931  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2932  }
2933  return $content;
2934  }
2935 
2944  public function stdWrap_csConv($content = '', $conf = [])
2945  {
2946  return $this->getTypoScriptFrontendController()->csConv($content, $conf['csConv']);
2947  }
2948 
2958  public function stdWrap_parseFunc($content = '', $conf = [])
2959  {
2960  return $this->parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
2961  }
2962 
2972  public function stdWrap_HTMLparser($content = '', $conf = [])
2973  {
2974  if (is_array($conf['HTMLparser.'])) {
2975  $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
2976  }
2977  return $content;
2978  }
2979 
2989  public function stdWrap_split($content = '', $conf = [])
2990  {
2991  return $this->splitObj($content, $conf['split.']);
2992  }
2993 
3002  public function stdWrap_replacement($content = '', $conf = [])
3003  {
3004  return $this->replacement($content, $conf['replacement.']);
3005  }
3006 
3016  public function stdWrap_prioriCalc($content = '', $conf = [])
3017  {
3018  $content = MathUtility::calculateWithParentheses($content);
3019  if ($conf['prioriCalc'] === 'intval') {
3020  $content = (int)$content;
3021  }
3022  return $content;
3023  }
3024 
3033  public function stdWrap_char($content = '', $conf = [])
3034  {
3035  return chr((int)$conf['char']);
3036  }
3037 
3045  public function stdWrap_intval($content = '')
3046  {
3047  return (int)$content;
3048  }
3049 
3058  public function stdWrap_hash($content = '', array $conf = [])
3059  {
3060  $algorithm = isset($conf['hash.']) ? $this->stdWrap($conf['hash'], $conf['hash.']) : $conf['hash'];
3061  if (function_exists('hash') && in_array($algorithm, hash_algos())) {
3062  return hash($algorithm, $content);
3063  }
3064  // Non-existing hashing algorithm
3065  return '';
3066  }
3067 
3076  public function stdWrap_round($content = '', $conf = [])
3077  {
3078  return $this->round($content, $conf['round.']);
3079  }
3080 
3089  public function stdWrap_numberFormat($content = '', $conf = [])
3090  {
3091  return $this->numberFormat($content, $conf['numberFormat.']);
3092  }
3093 
3101  public function stdWrap_expandList($content = '')
3102  {
3103  return GeneralUtility::expandList($content);
3104  }
3105 
3115  public function stdWrap_date($content = '', $conf = [])
3116  {
3117  // Check for zero length string to mimic default case of date/gmdate.
3118  $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
3119  $content = $conf['date.']['GMT'] ? gmdate($conf['date'], $content) : date($conf['date'], $content);
3120  return $content;
3121  }
3122 
3132  public function stdWrap_strftime($content = '', $conf = [])
3133  {
3134  // Check for zero length string to mimic default case of strtime/gmstrftime
3135  $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
3136  $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
3137  $tsfe = $this->getTypoScriptFrontendController();
3138  $tmp_charset = $conf['strftime.']['charset'] ? $conf['strftime.']['charset'] : $tsfe->localeCharset;
3139  if ($tmp_charset) {
3140  $content = $tsfe->csConv($content, $tmp_charset);
3141  }
3142  return $content;
3143  }
3144 
3153  public function stdWrap_strtotime($content = '', $conf = [])
3154  {
3155  if ($conf['strtotime'] !== '1') {
3156  $content .= ' ' . $conf['strtotime'];
3157  }
3158  return strtotime($content, $GLOBALS['EXEC_TIME']);
3159  }
3160 
3169  public function stdWrap_age($content = '', $conf = [])
3170  {
3171  return $this->calcAge((int)$GLOBALS['EXEC_TIME'] - (int)$content, $conf['age']);
3172  }
3173 
3183  public function stdWrap_case($content = '', $conf = [])
3184  {
3185  return $this->HTMLcaseshift($content, $conf['case']);
3186  }
3187 
3196  public function stdWrap_bytes($content = '', $conf = [])
3197  {
3198  return GeneralUtility::formatSize($content, $conf['bytes.']['labels'], $conf['bytes.']['base']);
3199  }
3200 
3209  public function stdWrap_substring($content = '', $conf = [])
3210  {
3211  return $this->substring($content, $conf['substring']);
3212  }
3213 
3221  public function stdWrap_removeBadHTML($content = '')
3222  {
3223  return $this->removeBadHTML($content);
3224  }
3225 
3234  public function stdWrap_cropHTML($content = '', $conf = [])
3235  {
3236  return $this->cropHTML($content, $conf['cropHTML']);
3237  }
3238 
3246  public function stdWrap_stripHtml($content = '')
3247  {
3248  return strip_tags($content);
3249  }
3250 
3259  public function stdWrap_crop($content = '', $conf = [])
3260  {
3261  return $this->crop($content, $conf['crop']);
3262  }
3263 
3271  public function stdWrap_rawUrlEncode($content = '')
3272  {
3273  return rawurlencode($content);
3274  }
3275 
3285  public function stdWrap_htmlSpecialChars($content = '', $conf = [])
3286  {
3287  if (!empty($conf['htmlSpecialChars.']['preserveEntities'])) {
3288  $content = htmlspecialchars($content, ENT_COMPAT, 'UTF-8', false);
3289  } else {
3290  $content = htmlspecialchars($content);
3291  }
3292  return $content;
3293  }
3294 
3303  public function stdWrap_encodeForJavaScriptValue($content = '')
3304  {
3305  return GeneralUtility::quoteJSvalue($content);
3306  }
3307 
3316  public function stdWrap_doubleBrTag($content = '', $conf = [])
3317  {
3318  return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'], $content);
3319  }
3320 
3330  public function stdWrap_br($content = '')
3331  {
3332  return nl2br($content, !empty($this->getTypoScriptFrontendController()->xhtmlDoctype));
3333  }
3334 
3343  public function stdWrap_brTag($content = '', $conf = [])
3344  {
3345  return str_replace(LF, $conf['brTag'], $content);
3346  }
3347 
3357  public function stdWrap_encapsLines($content = '', $conf = [])
3358  {
3359  return $this->encaps_lineSplit($content, $conf['encapsLines.']);
3360  }
3361 
3369  public function stdWrap_keywords($content = '')
3370  {
3371  return $this->keywords($content);
3372  }
3373 
3383  public function stdWrap_innerWrap($content = '', $conf = [])
3384  {
3385  return $this->wrap($content, $conf['innerWrap']);
3386  }
3387 
3397  public function stdWrap_innerWrap2($content = '', $conf = [])
3398  {
3399  return $this->wrap($content, $conf['innerWrap2']);
3400  }
3401 
3412  public function stdWrap_fontTag($content = '', $conf = [])
3413  {
3414  return $this->wrap($content, $conf['fontTag']);
3415  }
3416 
3425  public function stdWrap_addParams($content = '', $conf = [])
3426  {
3427  return $this->addParams($content, $conf['addParams.']);
3428  }
3429 
3439  public function stdWrap_textStyle($content = '', $conf = [])
3440  {
3441  return $this->textStyle($content, $conf['textStyle.']);
3442  }
3443 
3453  public function stdWrap_tableStyle($content = '', $conf = [])
3454  {
3455  return $this->tableStyle($content, $conf['tableStyle.']);
3456  }
3457 
3467  public function stdWrap_filelink($content = '', $conf = [])
3468  {
3469  return $this->filelink($content, $conf['filelink.']);
3470  }
3471 
3480  public function stdWrap_preCObject($content = '', $conf = [])
3481  {
3482  return $this->cObjGetSingle($conf['preCObject'], $conf['preCObject.'], '/stdWrap/.preCObject') . $content;
3483  }
3484 
3493  public function stdWrap_postCObject($content = '', $conf = [])
3494  {
3495  return $content . $this->cObjGetSingle($conf['postCObject'], $conf['postCObject.'], '/stdWrap/.postCObject');
3496  }
3497 
3507  public function stdWrap_wrapAlign($content = '', $conf = [])
3508  {
3509  $wrapAlign = trim($conf['wrapAlign']);
3510  if ($wrapAlign) {
3511  $content = $this->wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
3512  }
3513  return $content;
3514  }
3515 
3526  public function stdWrap_typolink($content = '', $conf = [])
3527  {
3528  return $this->typoLink($content, $conf['typolink.']);
3529  }
3530 
3539  public function stdWrap_TCAselectItem($content = '', $conf = [])
3540  {
3541  if (is_array($conf['TCAselectItem.'])) {
3542  $content = $this->TCAlookup($content, $conf['TCAselectItem.']);
3543  }
3544  return $content;
3545  }
3546 
3556  public function stdWrap_spaceBefore($content = '', $conf = [])
3557  {
3558  return $this->wrapSpace($content, trim($conf['spaceBefore']) . '|', $conf['space.']);
3559  }
3560 
3570  public function stdWrap_spaceAfter($content = '', $conf = [])
3571  {
3572  return $this->wrapSpace($content, '|' . trim($conf['spaceAfter']), $conf['space.']);
3573  }
3574 
3585  public function stdWrap_space($content = '', $conf = [])
3586  {
3587  return $this->wrapSpace($content, trim($conf['space']), $conf['space.']);
3588  }
3589 
3602  public function stdWrap_wrap($content = '', $conf = [])
3603  {
3604  return $this->wrap($content, $conf['wrap'], $conf['wrap.']['splitChar'] ? $conf['wrap.']['splitChar'] : '|');
3605  }
3606 
3616  public function stdWrap_noTrimWrap($content = '', $conf = [])
3617  {
3618  $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
3619  ? $this->stdWrap($conf['noTrimWrap.']['splitChar'], $conf['noTrimWrap.']['splitChar.'])
3620  : $conf['noTrimWrap.']['splitChar'];
3621  if ($splitChar === null || $splitChar === '') {
3622  $splitChar = '|';
3623  }
3624  $content = $this->noTrimWrap(
3625  $content,
3626  $conf['noTrimWrap'],
3627  $splitChar
3628  );
3629  return $content;
3630  }
3631 
3641  public function stdWrap_wrap2($content = '', $conf = [])
3642  {
3643  return $this->wrap($content, $conf['wrap2'], $conf['wrap2.']['splitChar'] ? $conf['wrap2.']['splitChar'] : '|');
3644  }
3645 
3655  public function stdWrap_dataWrap($content = '', $conf = [])
3656  {
3657  return $this->dataWrap($content, $conf['dataWrap']);
3658  }
3659 
3668  public function stdWrap_prepend($content = '', $conf = [])
3669  {
3670  return $this->cObjGetSingle($conf['prepend'], $conf['prepend.'], '/stdWrap/.prepend') . $content;
3671  }
3672 
3681  public function stdWrap_append($content = '', $conf = [])
3682  {
3683  return $content . $this->cObjGetSingle($conf['append'], $conf['append.'], '/stdWrap/.append');
3684  }
3685 
3695  public function stdWrap_wrap3($content = '', $conf = [])
3696  {
3697  return $this->wrap($content, $conf['wrap3'], $conf['wrap3.']['splitChar'] ? $conf['wrap3.']['splitChar'] : '|');
3698  }
3699 
3708  public function stdWrap_orderedStdWrap($content = '', $conf = [])
3709  {
3710  $sortedKeysArray = TemplateService::sortedKeyList($conf['orderedStdWrap.'], true);
3711  foreach ($sortedKeysArray as $key) {
3712  $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.']);
3713  }
3714  return $content;
3715  }
3716 
3725  public function stdWrap_outerWrap($content = '', $conf = [])
3726  {
3727  return $this->wrap($content, $conf['outerWrap']);
3728  }
3729 
3737  public function stdWrap_insertData($content = '')
3738  {
3739  return $this->insertData($content);
3740  }
3741 
3752  public function stdWrap_offsetWrap($content = '', $conf = [])
3753  {
3755 
3756  if (ExtensionManagementUtility::isLoaded('compatibility6')) {
3757  $controlTable = GeneralUtility::makeInstance(TYPO3\CMS\Compatibility6\ContentObject\OffsetTableContentObject::class);
3758  if ($conf['offsetWrap.']['tableParams'] || $conf['offsetWrap.']['tableParams.']) {
3759  $controlTable->tableParams = isset($conf['offsetWrap.']['tableParams.'])
3760  ? $this->stdWrap($conf['offsetWrap.']['tableParams'], $conf['offsetWrap.']['tableParams.'])
3761  : $conf['offsetWrap.']['tableParams'];
3762  }
3763  if ($conf['offsetWrap.']['tdParams'] || $conf['offsetWrap.']['tdParams.']) {
3764  $controlTable->tdParams = ' ' . (isset($conf['offsetWrap.']['tdParams.'])
3765  ? $this->stdWrap($conf['offsetWrap.']['tdParams'], $conf['offsetWrap.']['tdParams.'])
3766  : $conf['offsetWrap.']['tdParams']);
3767  }
3768  $content = $controlTable->start($content, $conf['offsetWrap']);
3769  if ($conf['offsetWrap.']['stdWrap.']) {
3770  $content = $this->stdWrap($content, $conf['offsetWrap.']['stdWrap.']);
3771  }
3772  }
3773 
3774  return $content;
3775  }
3776 
3785  public function stdWrap_postUserFunc($content = '', $conf = [])
3786  {
3787  return $this->callUserFunction($conf['postUserFunc'], $conf['postUserFunc.'], $content);
3788  }
3789 
3799  public function stdWrap_postUserFuncInt($content = '', $conf = [])
3800  {
3801  $substKey = 'INT_SCRIPT.' . $this->getTypoScriptFrontendController()->uniqueHash();
3802  $this->getTypoScriptFrontendController()->config['INTincScript'][$substKey] = [
3803  'content' => $content,
3804  'postUserFunc' => $conf['postUserFuncInt'],
3805  'conf' => $conf['postUserFuncInt.'],
3806  'type' => 'POSTUSERFUNC',
3807  'cObj' => serialize($this)
3808  ];
3809  $content = '<!--' . $substKey . '-->';
3810  return $content;
3811  }
3812 
3821  public function stdWrap_prefixComment($content = '', $conf = [])
3822  {
3823  if (!$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'] && !empty($conf['prefixComment'])) {
3824  $content = $this->prefixComment($conf['prefixComment'], [], $content);
3825  }
3826  return $content;
3827  }
3828 
3837  public function stdWrap_editIcons($content = '', $conf = [])
3838  {
3839  if ($this->getTypoScriptFrontendController()->beUserLogin && $conf['editIcons']) {
3840  if (!is_array($conf['editIcons.'])) {
3841  $conf['editIcons.'] = [];
3842  }
3843  $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
3844  }
3845  return $content;
3846  }
3847 
3856  public function stdWrap_editPanel($content = '', $conf = [])
3857  {
3858  if ($this->getTypoScriptFrontendController()->beUserLogin) {
3859  $content = $this->editPanel($content, $conf['editPanel.']);
3860  }
3861  return $content;
3862  }
3863 
3871  public function stdWrap_cacheStore($content = '', $conf = [])
3872  {
3873  if (!isset($conf['cache.'])) {
3874  return $content;
3875  }
3876  $key = $this->calculateCacheKey($conf['cache.']);
3877  if (empty($key)) {
3878  return $content;
3879  }
3881  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
3882  $tags = $this->calculateCacheTags($conf['cache.']);
3883  $lifetime = $this->calculateCacheLifetime($conf['cache.']);
3884  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'])) {
3885  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'] as $_funcRef) {
3886  $params = [
3887  'key' => $key,
3888  'content' => $content,
3889  'lifetime' => $lifetime,
3890  'tags' => $tags
3891  ];
3892  GeneralUtility::callUserFunction($_funcRef, $params, $this);
3893  }
3894  }
3895  $cacheFrontend->set($key, $content, $tags, $lifetime);
3896  return $content;
3897  }
3898 
3908  public function stdWrap_stdWrapPostProcess($content = '', $conf = [])
3909  {
3910  foreach ($this->stdWrapHookObjects as $hookObject) {
3912  $content = $hookObject->stdWrapPostProcess($content, $conf, $this);
3913  }
3914  return $content;
3915  }
3916 
3924  public function stdWrap_debug($content = '')
3925  {
3926  return '<pre>' . htmlspecialchars($content) . '</pre>';
3927  }
3928 
3937  public function stdWrap_debugFunc($content = '', $conf = [])
3938  {
3939  debug((int)$conf['debugFunc'] === 2 ? [$content] : $content);
3940  return $content;
3941  }
3942 
3950  public function stdWrap_debugData($content = '')
3951  {
3952  debug($this->data, '$cObj->data:');
3953  if (is_array($this->alternativeData)) {
3954  debug($this->alternativeData, '$this->alternativeData');
3955  }
3956  return $content;
3957  }
3958 
3968  public function numRows($conf)
3969  {
3970  $result = false;
3971  $conf['select.']['selectFields'] = 'count(*)';
3972  $res = $this->exec_getQuery($conf['table'], $conf['select.']);
3973  $db = $this->getDatabaseConnection();
3974  if ($error = $db->sql_error()) {
3975  $this->getTimeTracker()->setTSlogMessage($error, 3);
3976  } else {
3977  $row = $db->sql_fetch_row($res);
3978  $result = (int)$row[0];
3979  }
3980  $db->sql_free_result($res);
3981  return $result;
3982  }
3983 
3992  public function listNum($content, $listNum, $char)
3993  {
3994  $char = $char ?: ',';
3996  $char = chr($char);
3997  }
3998  $temp = explode($char, $content);
3999  $last = '' . (count($temp) - 1);
4000  // Take a random item if requested
4001  if ($listNum === 'rand') {
4002  $listNum = rand(0, count($temp) - 1);
4003  }
4004  $index = $this->calc(str_ireplace('last', $last, $listNum));
4005  return $temp[$index];
4006  }
4007 
4016  public function checkIf($conf)
4017  {
4018  if (!is_array($conf)) {
4019  return true;
4020  }
4021  if (isset($conf['directReturn'])) {
4022  return (bool)$conf['directReturn'];
4023  }
4024  $flag = true;
4025  if (isset($conf['isNull.'])) {
4026  $isNull = $this->stdWrap('', $conf['isNull.']);
4027  if ($isNull !== null) {
4028  $flag = false;
4029  }
4030  }
4031  if (isset($conf['isTrue']) || isset($conf['isTrue.'])) {
4032  $isTrue = isset($conf['isTrue.']) ? trim($this->stdWrap($conf['isTrue'], $conf['isTrue.'])) : trim($conf['isTrue']);
4033  if (!$isTrue) {
4034  $flag = false;
4035  }
4036  }
4037  if (isset($conf['isFalse']) || isset($conf['isFalse.'])) {
4038  $isFalse = isset($conf['isFalse.']) ? trim($this->stdWrap($conf['isFalse'], $conf['isFalse.'])) : trim($conf['isFalse']);
4039  if ($isFalse) {
4040  $flag = false;
4041  }
4042  }
4043  if (isset($conf['isPositive']) || isset($conf['isPositive.'])) {
4044  $number = isset($conf['isPositive.']) ? $this->calc($this->stdWrap($conf['isPositive'], $conf['isPositive.'])) : $this->calc($conf['isPositive']);
4045  if ($number < 1) {
4046  $flag = false;
4047  }
4048  }
4049  if ($flag) {
4050  $value = isset($conf['value.']) ? trim($this->stdWrap($conf['value'], $conf['value.'])) : trim($conf['value']);
4051  if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
4052  $number = isset($conf['isGreaterThan.']) ? trim($this->stdWrap($conf['isGreaterThan'], $conf['isGreaterThan.'])) : trim($conf['isGreaterThan']);
4053  if ($number <= $value) {
4054  $flag = false;
4055  }
4056  }
4057  if (isset($conf['isLessThan']) || isset($conf['isLessThan.'])) {
4058  $number = isset($conf['isLessThan.']) ? trim($this->stdWrap($conf['isLessThan'], $conf['isLessThan.'])) : trim($conf['isLessThan']);
4059  if ($number >= $value) {
4060  $flag = false;
4061  }
4062  }
4063  if (isset($conf['equals']) || isset($conf['equals.'])) {
4064  $number = isset($conf['equals.']) ? trim($this->stdWrap($conf['equals'], $conf['equals.'])) : trim($conf['equals']);
4065  if ($number != $value) {
4066  $flag = false;
4067  }
4068  }
4069  if (isset($conf['isInList']) || isset($conf['isInList.'])) {
4070  $number = isset($conf['isInList.']) ? trim($this->stdWrap($conf['isInList'], $conf['isInList.'])) : trim($conf['isInList']);
4071  if (!GeneralUtility::inList($value, $number)) {
4072  $flag = false;
4073  }
4074  }
4075  }
4076  if ($conf['negate']) {
4077  $flag = !$flag;
4078  }
4079  return $flag;
4080  }
4081 
4091  public function filelist($data)
4092  {
4093  $data = trim($data);
4094  if ($data === '') {
4095  return '';
4096  }
4097  $data_arr = explode('|', $data);
4098  // read directory:
4099  // MUST exist!
4100  $path = '';
4101  if ($this->getTypoScriptFrontendController()->lockFilePath) {
4102  // Cleaning name..., only relative paths accepted.
4103  $path = $this->clean_directory($data_arr[0]);
4104  // See if path starts with lockFilePath, the additional '/' is needed because clean_directory gets rid of it
4105  $path = GeneralUtility::isFirstPartOfStr($path . '/', $this->getTypoScriptFrontendController()->lockFilePath) ? $path : '';
4106  }
4107  if (!$path) {
4108  return '';
4109  }
4110  $items = [
4111  'files' => [],
4112  'sorting' => []
4113  ];
4114  $ext_list = strtolower(GeneralUtility::uniqueList($data_arr[1]));
4115  $sorting = trim($data_arr[2]);
4116  // Read dir:
4117  $d = @dir($path);
4118  if (is_object($d)) {
4119  $count = 0;
4120  while ($entry = $d->read()) {
4121  if ($entry != '.' && $entry != '..') {
4122  // Because of odd PHP-error where <br />-tag is sometimes placed after a filename!!
4123  $wholePath = $path . '/' . $entry;
4124  if (file_exists($wholePath) && filetype($wholePath) === 'file') {
4125  $info = GeneralUtility::split_fileref($wholePath);
4126  if (!$ext_list || GeneralUtility::inList($ext_list, $info['fileext'])) {
4127  $items['files'][] = $info['file'];
4128  switch ($sorting) {
4129  case 'name':
4130  $items['sorting'][] = strtolower($info['file']);
4131  break;
4132  case 'size':
4133  $items['sorting'][] = filesize($wholePath);
4134  break;
4135  case 'ext':
4136  $items['sorting'][] = $info['fileext'];
4137  break;
4138  case 'date':
4139  $items['sorting'][] = filectime($wholePath);
4140  break;
4141  case 'mdate':
4142  $items['sorting'][] = filemtime($wholePath);
4143  break;
4144  default:
4145  $items['sorting'][] = $count;
4146  }
4147  $count++;
4148  }
4149  }
4150  }
4151  }
4152  $d->close();
4153  }
4154  // Sort if required
4155  if (!empty($items['sorting'])) {
4156  if (strtolower(trim($data_arr[3])) != 'r') {
4157  asort($items['sorting']);
4158  } else {
4159  arsort($items['sorting']);
4160  }
4161  }
4162  if (!empty($items['files'])) {
4163  // Make list
4164  reset($items['sorting']);
4165  $fullPath = trim($data_arr[4]);
4166  $list_arr = [];
4167  foreach ($items['sorting'] as $key => $v) {
4168  $list_arr[] = $fullPath ? $path . '/' . $items['files'][$key] : $items['files'][$key];
4169  }
4170  return implode(',', $list_arr);
4171  }
4172  return '';
4173  }
4174 
4183  public function clean_directory($theDir)
4184  {
4185  // proceeds if no '//', '..' or '\' is in the $theFile
4186  if (GeneralUtility::validPathStr($theDir)) {
4187  // Removes all dots, slashes and spaces after a path...
4188  $theDir = preg_replace('/[\\/\\. ]*$/', '', $theDir);
4189  if (!GeneralUtility::isAbsPath($theDir) && @is_dir($theDir)) {
4190  return $theDir;
4191  }
4192  }
4193  return '';
4194  }
4195 
4206  public function HTMLparser_TSbridge($theValue, $conf)
4207  {
4208  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
4209  $htmlParserCfg = $htmlParser->HTMLparserConfig($conf);
4210  return $htmlParser->HTMLcleaner($theValue, $htmlParserCfg[0], $htmlParserCfg[1], $htmlParserCfg[2], $htmlParserCfg[3]);
4211  }
4212 
4221  public function dataWrap($content, $wrap)
4222  {
4223  return $this->wrap($content, $this->insertData($wrap));
4224  }
4225 
4234  public function insertData($str)
4235  {
4236  $inside = 0;
4237  $newVal = '';
4238  $pointer = 0;
4239  $totalLen = strlen($str);
4240  do {
4241  if (!$inside) {
4242  $len = strcspn(substr($str, $pointer), '{');
4243  $newVal .= substr($str, $pointer, $len);
4244  $inside = 1;
4245  } else {
4246  $len = strcspn(substr($str, $pointer), '}') + 1;
4247  $newVal .= $this->getData(substr($str, $pointer + 1, $len - 2), $this->data);
4248  $inside = 0;
4249  }
4250  $pointer += $len;
4251  } while ($pointer < $totalLen);
4252  return $newVal;
4253  }
4254 
4265  public function prefixComment($str, $conf, $content)
4266  {
4267  if (empty($str)) {
4268  return $content;
4269  }
4270  $parts = explode('|', $str);
4271  $indent = (int)$parts[0];
4272  $comment = htmlspecialchars($this->insertData($parts[1]));
4273  $output = LF
4274  . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [begin] -->' . LF
4275  . str_pad('', ($indent + 1), TAB) . $content . LF
4276  . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [end] -->' . LF
4277  . str_pad('', ($indent + 1), TAB);
4278  return $output;
4279  }
4280 
4290  public function substring($content, $options)
4291  {
4292  $tsfe = $this->getTypoScriptFrontendController();
4293  $options = GeneralUtility::intExplode(',', $options . ',');
4294  if ($options[1]) {
4295  return $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $options[0], $options[1]);
4296  } else {
4297  return $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $options[0]);
4298  }
4299  }
4300 
4310  public function crop($content, $options)
4311  {
4312  $options = explode('|', $options);
4313  $chars = (int)$options[0];
4314  $afterstring = trim($options[1]);
4315  $crop2space = trim($options[2]);
4316  if ($chars) {
4317  $tsfe = $this->getTypoScriptFrontendController();
4318  if ($tsfe->csConvObj->strlen($tsfe->renderCharset, $content) > abs($chars)) {
4319  $truncatePosition = false;
4320  if ($chars < 0) {
4321  $content = $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $chars);
4322  if ($crop2space) {
4323  $truncatePosition = strpos($content, ' ');
4324  }
4325  $content = $truncatePosition ? $afterstring . substr($content, $truncatePosition) : $afterstring . $content;
4326  } else {
4327  $content = $tsfe->csConvObj->substr($tsfe->renderCharset, $content, 0, $chars);
4328  if ($crop2space) {
4329  $truncatePosition = strrpos($content, ' ');
4330  }
4331  $content = $truncatePosition ? substr($content, 0, $truncatePosition) . $afterstring : $content . $afterstring;
4332  }
4333  }
4334  }
4335  return $content;
4336  }
4337 
4351  public function cropHTML($content, $options)
4352  {
4353  $options = explode('|', $options);
4354  $chars = (int)$options[0];
4355  $absChars = abs($chars);
4356  $replacementForEllipsis = trim($options[1]);
4357  $crop2space = trim($options[2]) === '1';
4358  // Split $content into an array(even items in the array are outside the tags, odd numbers are tag-blocks).
4359  $tags = 'a|abbr|address|area|article|aside|audio|b|bdi|bdo|blockquote|body|br|button|caption|cite|code|col|colgroup|data|datalist|dd|del|dfn|div|dl|dt|em|embed|fieldset|figcaption|figure|font|footer|form|h1|h2|h3|h4|h5|h6|header|hr|i|iframe|img|input|ins|kbd|keygen|label|legend|li|link|main|map|mark|meter|nav|object|ol|optgroup|option|output|p|param|pre|progress|q|rb|rp|rt|rtc|ruby|s|samp|section|select|small|source|span|strong|sub|sup|table|tbody|td|textarea|tfoot|th|thead|time|tr|track|u|ul|ut|var|video|wbr';
4360  $tagsRegEx = '
4361  (
4362  (?:
4363  <!--.*?--> # a comment
4364  |
4365  <canvas[^>]*>.*?</canvas> # a canvas tag
4366  |
4367  <script[^>]*>.*?</script> # a script tag
4368  |
4369  <noscript[^>]*>.*?</noscript> # a noscript tag
4370  |
4371  <template[^>]*>.*?</template> # a template tag
4372  )
4373  |
4374  </?(?:' . $tags . ')+ # opening tag (\'<tag\') or closing tag (\'</tag\')
4375  (?:
4376  (?:
4377  (?:
4378  \\s+\\w[\\w-]* # EITHER spaces, followed by attribute names
4379  (?:
4380  \\s*=?\\s* # equals
4381  (?>
4382  ".*?" # attribute values in double-quotes
4383  |
4384  \'.*?\' # attribute values in single-quotes
4385  |
4386  [^\'">\\s]+ # plain attribute values
4387  )
4388  )?
4389  )
4390  | # OR a single dash (for TYPO3 link tag)
4391  (?:
4392  \\s+-
4393  )
4394  )+\\s*
4395  | # OR only spaces
4396  \\s*
4397  )
4398  /?> # closing the tag with \'>\' or \'/>\'
4399  )';
4400  $splittedContent = preg_split('%' . $tagsRegEx . '%xs', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
4401  // Reverse array if we are cropping from right.
4402  if ($chars < 0) {
4403  $splittedContent = array_reverse($splittedContent);
4404  }
4405  // Crop the text (chars of tag-blocks are not counted).
4406  $strLen = 0;
4407  // This is the offset of the content item which was cropped.
4408  $croppedOffset = null;
4409  $tsfe = $this->getTypoScriptFrontendController();
4410  $countSplittedContent = count($splittedContent);
4411  for ($offset = 0; $offset < $countSplittedContent; $offset++) {
4412  if ($offset % 2 === 0) {
4413  $tempContent = $tsfe->csConvObj->utf8_encode($splittedContent[$offset], $tsfe->renderCharset);
4414  $thisStrLen = $tsfe->csConvObj->strlen('utf-8', html_entity_decode($tempContent, ENT_COMPAT, 'UTF-8'));
4415  if ($strLen + $thisStrLen > $absChars) {
4416  $croppedOffset = $offset;
4417  $cropPosition = $absChars - $strLen;
4418  // The snippet "&[^&\s;]{2,8};" in the RegEx below represents entities.
4419  $patternMatchEntityAsSingleChar = '(&[^&\\s;]{2,8};|.)';
4420  $cropRegEx = $chars < 0 ? '#' . $patternMatchEntityAsSingleChar . '{0,' . ($cropPosition + 1) . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . ($cropPosition + 1) . '}#uis';
4421  if (preg_match($cropRegEx, $tempContent, $croppedMatch)) {
4422  $tempContentPlusOneCharacter = $croppedMatch[0];
4423  } else {
4424  $tempContentPlusOneCharacter = false;
4425  }
4426  $cropRegEx = $chars < 0 ? '#' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}#uis';
4427  if (preg_match($cropRegEx, $tempContent, $croppedMatch)) {
4428  $tempContent = $croppedMatch[0];
4429  if ($crop2space && $tempContentPlusOneCharacter !== false) {
4430  $cropRegEx = $chars < 0 ? '#(?<=\\s)' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}(?=\\s)#uis';
4431  if (preg_match($cropRegEx, $tempContentPlusOneCharacter, $croppedMatch)) {
4432  $tempContent = $croppedMatch[0];
4433  }
4434  }
4435  }
4436  $splittedContent[$offset] = $tsfe->csConvObj->utf8_decode($tempContent, $tsfe->renderCharset);
4437  break;
4438  } else {
4439  $strLen += $thisStrLen;
4440  }
4441  }
4442  }
4443  // Close cropped tags.
4444  $closingTags = [];
4445  if ($croppedOffset !== null) {
4446  $openingTagRegEx = '#^<(\\w+)(?:\\s|>)#';
4447  $closingTagRegEx = '#^</(\\w+)(?:\\s|>)#';
4448  for ($offset = $croppedOffset - 1; $offset >= 0; $offset = $offset - 2) {
4449  if (substr($splittedContent[$offset], -2) === '/>') {
4450  // Ignore empty element tags (e.g. <br />).
4451  continue;
4452  }
4453  preg_match($chars < 0 ? $closingTagRegEx : $openingTagRegEx, $splittedContent[$offset], $matches);
4454  $tagName = isset($matches[1]) ? $matches[1] : null;
4455  if ($tagName !== null) {
4456  // Seek for the closing (or opening) tag.
4457  $countSplittedContent = count($splittedContent);
4458  for ($seekingOffset = $offset + 2; $seekingOffset < $countSplittedContent; $seekingOffset = $seekingOffset + 2) {
4459  preg_match($chars < 0 ? $openingTagRegEx : $closingTagRegEx, $splittedContent[$seekingOffset], $matches);
4460  $seekingTagName = isset($matches[1]) ? $matches[1] : null;
4461  if ($tagName === $seekingTagName) {
4462  // We found a matching tag.
4463  // Add closing tag only if it occurs after the cropped content item.
4464  if ($seekingOffset > $croppedOffset) {
4465  $closingTags[] = $splittedContent[$seekingOffset];
4466  }
4467  break;
4468  }
4469  }
4470  }
4471  }
4472  // Drop the cropped items of the content array. The $closingTags will be added later on again.
4473  array_splice($splittedContent, $croppedOffset + 1);
4474  }
4475  $splittedContent = array_merge($splittedContent, [
4476  $croppedOffset !== null ? $replacementForEllipsis : ''
4477  ], $closingTags);
4478  // Reverse array once again if we are cropping from the end.
4479  if ($chars < 0) {
4480  $splittedContent = array_reverse($splittedContent);
4481  }
4482  return implode('', $splittedContent);
4483  }
4484 
4493  public function removeBadHTML($text)
4494  {
4495  // Copyright 2002-2003 Thomas Bley
4496  $text = preg_replace([
4497  '\'<script[^>]*?>.*?</script[^>]*?>\'si',
4498  '\'<applet[^>]*?>.*?</applet[^>]*?>\'si',
4499  '\'<object[^>]*?>.*?</object[^>]*?>\'si',
4500  '\'<iframe[^>]*?>.*?</iframe[^>]*?>\'si',
4501  '\'<frameset[^>]*?>.*?</frameset[^>]*?>\'si',
4502  '\'<style[^>]*?>.*?</style[^>]*?>\'si',
4503  '\'<marquee[^>]*?>.*?</marquee[^>]*?>\'si',
4504  '\'<script[^>]*?>\'si',
4505  '\'<meta[^>]*?>\'si',
4506  '\'<base[^>]*?>\'si',
4507  '\'<applet[^>]*?>\'si',
4508  '\'<object[^>]*?>\'si',
4509  '\'<link[^>]*?>\'si',
4510  '\'<iframe[^>]*?>\'si',
4511  '\'<frame[^>]*?>\'si',
4512  '\'<frameset[^>]*?>\'si',
4513  '\'<input[^>]*?>\'si',
4514  '\'<form[^>]*?>\'si',
4515  '\'<embed[^>]*?>\'si',
4516  '\'background-image:url\'si',
4517  '\'<\\w+.*?(onabort|onbeforeunload|onblur|onchange|onclick|ondblclick|ondragdrop|onerror|onfilterchange|onfocus|onhelp|onkeydown|onkeypress|onkeyup|onload|onmousedown|onmousemove|onmouseout|onmouseover|onmouseup|onmove|onreadystatechange|onreset|onresize|onscroll|onselect|onselectstart|onsubmit|onunload).*?>\'si'
4518  ], '', $text);
4519  $text = preg_replace('/<a[^>]*href[[:space:]]*=[[:space:]]*["\']?[[:space:]]*javascript[^>]*/i', '', $text);
4520  // Return clean content
4521  return $text;
4522  }
4523 
4534  public function textStyle($theValue, $conf)
4535  {
4536  $this->getTypoScriptFrontendController()->logDeprecatedTyposcript('textStyle', 'Deprecated since 7.1 and will be removed with CMS 8. Use CSS instead');
4537  $conf['face.'][1] = 'Times New Roman';
4538  $conf['face.'][2] = 'Verdana,Arial,Helvetica,Sans serif';
4539  $conf['face.'][3] = 'Arial,Helvetica,Sans serif';
4540  $conf['size.'][1] = 1;
4541  $conf['size.'][2] = 2;
4542  $conf['size.'][3] = 3;
4543  $conf['size.'][4] = 4;
4544  $conf['size.'][5] = 5;
4545  $conf['size.'][10] = '+1';
4546  $conf['size.'][11] = '-1';
4547  $conf['color.'][240] = 'black';
4548  $conf['color.'][241] = 'white';
4549  $conf['color.'][242] = '#333333';
4550  $conf['color.'][243] = 'gray';
4551  $conf['color.'][244] = 'silver';
4552  $conf['color.'][245] = 'red';
4553  $conf['color.'][246] = 'navy';
4554  $conf['color.'][247] = 'yellow';
4555  $conf['color.'][248] = 'green';
4556  $conf['color.'][249] = 'olive';
4557  $conf['color.'][250] = 'maroon';
4558  $face = $this->data[$conf['face.']['field']];
4559  $size = $this->data[$conf['size.']['field']];
4560  $color = $this->data[$conf['color.']['field']];
4561  $align = $this->data[$conf['align.']['field']];
4562  $properties = $this->data[$conf['properties.']['field']];
4563  if (!$properties) {
4564  $properties = isset($conf['properties.']['default.']) ? $this->stdWrap($conf['properties.']['default'], $conf['properties.']['default.']) : $conf['properties.']['default'];
4565  }
4566  // Properties
4567  if ($properties & 8) {
4568  $theValue = $this->HTMLcaseshift($theValue, 'upper');
4569  }
4570  if ($properties & 1) {
4571  $theValue = '<strong>' . $theValue . '</strong>';
4572  }
4573  if ($properties & 2) {
4574  $theValue = '<i>' . $theValue . '</i>';
4575  }
4576  if ($properties & 4) {
4577  $theValue = '<u>' . $theValue . '</u>';
4578  }
4579  // Fonttag
4580  $theFace = $conf['face.'][$face];
4581  if (!$theFace) {
4582  $theFace = isset($conf['face.']['default.']) ? $this->stdWrap($conf['face.']['default'], $conf['face.']['default.']) : $conf['face.']['default'];
4583  }
4584  $theSize = $conf['size.'][$size];
4585  if (!$theSize) {
4586  $theSize = isset($conf['size.']['default.']) ? $this->stdWrap($conf['size.']['default'], $conf['size.']['default.']) : $conf['size.']['default'];
4587  }
4588  $theColor = $conf['color.'][$color];
4589  if (!$theColor) {
4590  $theColor = isset($conf['color.']['default.']) ? $this->stdWrap($conf['color.']['default'], $conf['color.']['default.']) : $conf['color.']['default.'];
4591  }
4592  if ($conf['altWrap']) {
4593  $theValue = $this->wrap($theValue, $conf['altWrap']);
4594  } elseif ($theFace || $theSize || $theColor) {
4595  $fontWrap = '<font' . ($theFace ? ' face="' . $theFace . '"' : '') . ($theSize ? ' size="' . $theSize . '"' : '') . ($theColor ? ' color="' . $theColor . '"' : '') . '>|</font>';
4596  $theValue = $this->wrap($theValue, $fontWrap);
4597  }
4598  // Align
4599  if ($align) {
4600  $theValue = $this->wrap($theValue, '<div style="text-align:' . $align . ';">|</div>');
4601  }
4602  // Return
4603  return $theValue;
4604  }
4605 
4616  public function tableStyle($theValue, $conf)
4617  {
4618  $this->getTypoScriptFrontendController()->logDeprecatedTyposcript('tableStyle', 'Deprecated since 7.1 and will be removed with CMS 8. Use CSS instead');
4619  $conf['color.'][240] = 'black';
4620  $conf['color.'][241] = 'white';
4621  $conf['color.'][242] = '#333333';
4622  $conf['color.'][243] = 'gray';
4623  $conf['color.'][244] = 'silver';
4624  $align = isset($conf['align.']) ? $this->stdWrap($conf['align'], $conf['align.']) : $conf['align'];
4625  $border = isset($conf['border.']) ? (int)$this->stdWrap($conf['border'], $conf['border.']) : (int)$conf['border'];
4626  $cellspacing = isset($conf['cellspacing.']) ? (int)$this->stdWrap($conf['cellspacing'], $conf['cellspacing.']) : (int)$conf['cellspacing'];
4627  $cellpadding = isset($conf['cellpadding.']) ? (int)$this->stdWrap($conf['cellpadding'], $conf['cellpadding.']) : (int)$conf['cellpadding'];
4628  $color = $this->data[$conf['color.']['field']];
4629  $theColor = $conf['color.'][$color] ? $conf['color.'][$color] : $conf['color.']['default'];
4630  // Assembling the table tag
4631  $tableTagArray = [
4632  '<table'
4633  ];
4634  $tableTagArray[] = 'border="' . $border . '"';
4635  $tableTagArray[] = 'cellspacing="' . $cellspacing . '"';
4636  $tableTagArray[] = 'cellpadding="' . $cellpadding . '"';
4637  if ($align) {
4638  $tableTagArray[] = 'align="' . $align . '"';
4639  }
4640  if ($theColor) {
4641  $tableTagArray[] = 'bgcolor="' . $theColor . '"';
4642  }
4643  if ($conf['params']) {
4644  $tableTagArray[] = $conf['params'];
4645  }
4646  $tableWrap = implode(' ', $tableTagArray) . '> | </table>';
4647  $theValue = $this->wrap($theValue, $tableWrap);
4648  // return
4649  return $theValue;
4650  }
4651 
4660  public function addParams($content, $conf)
4661  {
4662  // For XHTML compliance.
4663  $lowerCaseAttributes = true;
4664  if (!is_array($conf)) {
4665  return $content;
4666  }
4667  $key = 1;
4668  $parts = explode('<', $content);
4669  if ((int)$conf['_offset']) {
4670  $key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
4671  }
4672  $subparts = explode('>', $parts[$key]);
4673  if (trim($subparts[0])) {
4674  // Get attributes and name
4675  $attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
4676  list($tagName) = explode(' ', $subparts[0], 2);
4677  // adds/overrides attributes
4678  foreach ($conf as $pkey => $val) {
4679  if (substr($pkey, -1) !== '.' && $pkey[0] !== '_') {
4680  $tmpVal = isset($conf[$pkey . '.']) ? $this->stdWrap($conf[$pkey], $conf[$pkey . '.']) : (string)$val;
4681  if ($lowerCaseAttributes) {
4682  $pkey = strtolower($pkey);
4683  }
4684  if ($tmpVal !== '') {
4685  $attribs[$pkey] = $tmpVal;
4686  }
4687  }
4688  }
4689  // Re-assembles the tag and content
4690  $subparts[0] = trim($tagName . ' ' . GeneralUtility::implodeAttributes($attribs));
4691  $parts[$key] = implode('>', $subparts);
4692  $content = implode('<', $parts);
4693  }
4694  return $content;
4695  }
4696 
4707  public function filelink($theValue, $conf)
4708  {
4709  $conf['path'] = isset($conf['path.']) ? $this->stdWrap($conf['path'], $conf['path.']) : $conf['path'];
4710  $theFile = trim($conf['path']) . $theValue;
4711  if (!@is_file($theFile)) {
4712  return '';
4713  }
4714  $theFileEnc = str_replace('%2F', '/', rawurlencode($theFile));
4715  $title = $conf['title'];
4716  if (isset($conf['title.'])) {
4717  $title = $this->stdWrap($title, $conf['title.']);
4718  }
4719  $target = $conf['target'];
4720  if (isset($conf['target.'])) {
4721  $target = $this->stdWrap($target, $conf['target.']);
4722  }
4723  $tsfe = $this->getTypoScriptFrontendController();
4724 
4725  $typoLinkConf = [
4726  'parameter' => $theFileEnc,
4727  'fileTarget' => $target,
4728  'title' => $title,
4729  'ATagParams' => $this->getATagParams($conf)
4730  ];
4731 
4732  if (isset($conf['typolinkConfiguration.'])) {
4733  $additionalTypoLinkConfiguration = $conf['typolinkConfiguration.'];
4734  // We only allow additional configuration. This is why the generated conf overwrites the additional conf.
4735  ArrayUtility::mergeRecursiveWithOverrule($additionalTypoLinkConfiguration, $typoLinkConf);
4736  $typoLinkConf = $additionalTypoLinkConfiguration;
4737  }
4738 
4739  if (isset($conf['jumpurl']) || isset($conf['jumpurl.'])) {
4740  GeneralUtility::deprecationLog('The TypoScript jumpurl configuration is deprecated for file links since TYPO3 CMS 7 and will be removed in TYPO3 CMS 8. Pass this configuration in the typolinkConfiguration property instead.');
4741  if (isset($conf['jumpurl'])) {
4742  $typoLinkConf['jumpurl'] = $conf['jumpurl'];
4743  }
4744  if (isset($conf['jumpurl.'])) {
4745  $typoLinkConf['jumpurl.'] = $conf['jumpurl.'];
4746  }
4747  }
4748 
4749  $theLinkWrap = $this->typoLink('|', $typoLinkConf);
4750  $theSize = filesize($theFile);
4751  $fI = GeneralUtility::split_fileref($theFile);
4752  $icon = '';
4753  if ($conf['icon']) {
4754  $conf['icon.']['path'] = isset($conf['icon.']['path.'])
4755  ? $this->stdWrap($conf['icon.']['path'], $conf['icon.']['path.'])
4756  : $conf['icon.']['path'];
4757  $iconP = !empty($conf['icon.']['path'])
4758  ? $conf['icon.']['path']
4759  : ExtensionManagementUtility::siteRelPath('frontend') . 'Resources/Public/Icons/FileIcons/';
4760  $conf['icon.']['ext'] = isset($conf['icon.']['ext.'])
4761  ? $this->stdWrap($conf['icon.']['ext'], $conf['icon.']['ext.'])
4762  : $conf['icon.']['ext'];
4763  $iconExt = !empty($conf['icon.']['ext']) ? '.' . $conf['icon.']['ext'] : '.gif';
4764  $icon = @is_file(($iconP . $fI['fileext'] . $iconExt))
4765  ? $iconP . $fI['fileext'] . $iconExt
4766  : $iconP . 'default' . $iconExt;
4767  // Checking for images: If image, then return link to thumbnail.
4768  $IEList = isset($conf['icon_image_ext_list.']) ? $this->stdWrap($conf['icon_image_ext_list'], $conf['icon_image_ext_list.']) : $conf['icon_image_ext_list'];
4769  $image_ext_list = str_replace(' ', '', strtolower($IEList));
4770  if ($fI['fileext'] && GeneralUtility::inList($image_ext_list, $fI['fileext'])) {
4771  if ($conf['iconCObject']) {
4772  $icon = $this->cObjGetSingle($conf['iconCObject'], $conf['iconCObject.'], 'iconCObject');
4773  } else {
4774  $notFoundThumb = ExtensionManagementUtility::siteRelPath('core') . 'Resources/Public/Images/NotFound.gif';
4775  $sizeParts = [64, 64];
4776  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']) {
4777  // using the File Abstraction Layer to generate a preview image
4778  try {
4780  $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($theFile);
4781  if ($fileObject->isMissing()) {
4782  $icon = $notFoundThumb;
4783  } else {
4784  $fileExtension = $fileObject->getExtension();
4785  if ($fileExtension === 'ttf' || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
4786  if ($conf['icon_thumbSize'] || $conf['icon_thumbSize.']) {
4787  $thumbSize = isset($conf['icon_thumbSize.']) ? $this->stdWrap($conf['icon_thumbSize'], $conf['icon_thumbSize.']) : $conf['icon_thumbSize'];
4788  $sizeParts = explode('x', $thumbSize);
4789  }
4790  $icon = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, [
4791  'width' => $sizeParts[0],
4792  'height' => $sizeParts[1]
4793  ])->getPublicUrl(true);
4794  }
4795  }
4796  } catch (ResourceDoesNotExistException $exception) {
4797  $icon = $notFoundThumb;
4798  }
4799  } else {
4800  $icon = $notFoundThumb;
4801  }
4802  $urlPrefix = '';
4803  if (parse_url($icon, PHP_URL_HOST) === null) {
4804  $urlPrefix = $tsfe->absRefPrefix;
4805  }
4806  $icon = '<img src="' . htmlspecialchars($urlPrefix . $icon) . '"' .
4807  ' width="' . (int)$sizeParts[0] . '" height="' . (int)$sizeParts[1] . '" ' .
4808  $this->getBorderAttr(' border="0"') . '' . $this->getAltParam($conf) . ' />';
4809  }
4810  } else {
4811  $conf['icon.']['widthAttribute'] = isset($conf['icon.']['widthAttribute.'])
4812  ? $this->stdWrap($conf['icon.']['widthAttribute'], $conf['icon.']['widthAttribute.'])
4813  : $conf['icon.']['widthAttribute'];
4814  $iconWidth = !empty($conf['icon.']['widthAttribute']) ? $conf['icon.']['widthAttribute'] : 18;
4815  $conf['icon.']['heightAttribute'] = isset($conf['icon.']['heightAttribute.'])
4816  ? $this->stdWrap($conf['icon.']['heightAttribute'], $conf['icon.']['heightAttribute.'])
4817  : $conf['icon.']['heightAttribute'];
4818  $iconHeight = !empty($conf['icon.']['heightAttribute']) ? (int)$conf['icon.']['heightAttribute'] : 16;
4819  $icon = '<img src="' . htmlspecialchars($tsfe->absRefPrefix . $icon) . '" width="' . (int)$iconWidth . '" height="' . (int)$iconHeight . '"'
4820  . $this->getBorderAttr(' border="0"') . $this->getAltParam($conf) . ' />';
4821  }
4822  if ($conf['icon_link'] && !$conf['combinedLink']) {
4823  $icon = $this->wrap($icon, $theLinkWrap);
4824  }
4825  $icon = isset($conf['icon.']) ? $this->stdWrap($icon, $conf['icon.']) : $icon;
4826  }
4827  $size = '';
4828  if ($conf['size']) {
4829  $size = isset($conf['size.']) ? $this->stdWrap($theSize, $conf['size.']) : $theSize;
4830  }
4831  // Wrapping file label
4832  if ($conf['removePrependedNumbers']) {
4833  $theValue = preg_replace('/_[0-9][0-9](\\.[[:alnum:]]*)$/', '\\1', $theValue);
4834  }
4835  if (isset($conf['labelStdWrap.'])) {
4836  $theValue = $this->stdWrap($theValue, $conf['labelStdWrap.']);
4837  }
4838  // Wrapping file
4839  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
4840  if ($conf['combinedLink']) {
4841  $theValue = $icon . $theValue;
4842  if ($conf['ATagBeforeWrap']) {
4843  $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
4844  } else {
4845  $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
4846  }
4847  $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
4848  // output
4849  $output = $file . $size;
4850  } else {
4851  if ($conf['ATagBeforeWrap']) {
4852  $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
4853  } else {
4854  $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
4855  }
4856  $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
4857  // output
4858  $output = $icon . $file . $size;
4859  }
4860  if (isset($conf['stdWrap.'])) {
4861  $output = $this->stdWrap($output, $conf['stdWrap.']);
4862  }
4863  return $output;
4864  }
4865 
4873  public function calc($val)
4874  {
4875  $parts = GeneralUtility::splitCalc($val, '+-*/');
4876  $value = 0;
4877  foreach ($parts as $part) {
4878  $theVal = $part[1];
4879  $sign = $part[0];
4880  if ((string)(int)$theVal === (string)$theVal) {
4881  $theVal = (int)$theVal;
4882  } else {
4883  $theVal = 0;
4884  }
4885  if ($sign === '-') {
4886  $value -= $theVal;
4887  }
4888  if ($sign === '+') {
4889  $value += $theVal;
4890  }
4891  if ($sign === '/') {
4892  if ((int)$theVal) {
4893  $value /= (int)$theVal;
4894  }
4895  }
4896  if ($sign === '*') {
4897  $value *= $theVal;
4898  }
4899  }
4900  return $value;
4901  }
4902 
4912  public function calcIntExplode($delim, $string)
4913  {
4914  $temp = explode($delim, $string);
4915  foreach ($temp as $key => $val) {
4916  $temp[$key] = (int)$this->calc($val);
4917  }
4918  return $temp;
4919  }
4920 
4932  public function splitObj($value, $conf)
4933  {
4934  $conf['token'] = isset($conf['token.']) ? $this->stdWrap($conf['token'], $conf['token.']) : $conf['token'];
4935  if ($conf['token'] === '') {
4936  return $value;
4937  }
4938  $valArr = explode($conf['token'], $value);
4939 
4940  // return value directly by returnKey. No further processing
4941  if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey']) || $conf['returnKey.'])) {
4942  $key = isset($conf['returnKey.']) ? (int)$this->stdWrap($conf['returnKey'], $conf['returnKey.']) : (int)$conf['returnKey'];
4943  return isset($valArr[$key]) ? $valArr[$key] : '';
4944  }
4945 
4946  // return the amount of elements. No further processing
4947  if (!empty($valArr) && ($conf['returnCount'] || $conf['returnCount.'])) {
4948  $returnCount = isset($conf['returnCount.']) ? (bool)$this->stdWrap($conf['returnCount'], $conf['returnCount.']) : (bool)$conf['returnCount'];
4949  return $returnCount ? count($valArr) : 0;
4950  }
4951 
4952  // calculate splitCount
4953  $splitCount = count($valArr);
4954  $max = isset($conf['max.']) ? (int)$this->stdWrap($conf['max'], $conf['max.']) : (int)$conf['max'];
4955  if ($max && $splitCount > $max) {
4956  $splitCount = $max;
4957  }
4958  $min = isset($conf['min.']) ? (int)$this->stdWrap($conf['min'], $conf['min.']) : (int)$conf['min'];
4959  if ($min && $splitCount < $min) {
4960  $splitCount = $min;
4961  }
4962  $wrap = isset($conf['wrap.']) ? (string)$this->stdWrap($conf['wrap'], $conf['wrap.']) : (string)$conf['wrap'];
4963  $cObjNumSplitConf = isset($conf['cObjNum.']) ? (string)$this->stdWrap($conf['cObjNum'], $conf['cObjNum.']) : (string)$conf['cObjNum'];
4964  $splitArr = [];
4965  if ($wrap !== '' || $cObjNumSplitConf !== '') {
4966  $splitArr['wrap'] = $wrap;
4967  $splitArr['cObjNum'] = $cObjNumSplitConf;
4968  $splitArr = $GLOBALS['TSFE']->tmpl->splitConfArray($splitArr, $splitCount);
4969  }
4970  $content = '';
4971  for ($a = 0; $a < $splitCount; $a++) {
4972  $this->getTypoScriptFrontendController()->register['SPLIT_COUNT'] = $a;
4973  $value = '' . $valArr[$a];
4974  $this->data[$this->currentValKey] = $value;
4975  if ($splitArr[$a]['cObjNum']) {
4976  $objName = (int)$splitArr[$a]['cObjNum'];
4977  $value = isset($conf[$objName . '.'])
4978  ? $this->stdWrap($this->cObjGet($conf[$objName . '.'], $objName . '.'), $conf[$objName . '.'])
4979  : $this->cObjGet($conf[$objName . '.'], $objName . '.');
4980  }
4981  $wrap = isset($splitArr[$a]['wrap.']) ? $this->stdWrap($splitArr[$a]['wrap'], $splitArr[$a]['wrap.']) : $splitArr[$a]['wrap'];
4982  if ($wrap) {
4983  $value = $this->wrap($value, $wrap);
4984  }
4985  $content .= $value;
4986  }
4987  return $content;
4988  }
4989 
4997  protected function replacement($content, array $configuration)
4998  {
4999  // Sorts actions in configuration by numeric index
5000  ksort($configuration, SORT_NUMERIC);
5001  foreach ($configuration as $index => $action) {
5002  // Checks whether we have an valid action and a numeric key ending with a dot ("10.")
5003  if (is_array($action) && substr($index, -1) === '.' && MathUtility::canBeInterpretedAsInteger(substr($index, 0, -1))) {
5004  $content = $this->replacementSingle($content, $action);
5005  }
5006  }
5007  return $content;
5008  }
5009 
5017  protected function replacementSingle($content, array $configuration)
5018  {
5019  if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
5020  // Gets the strings
5021  $search = isset($configuration['search.']) ? $this->stdWrap($configuration['search'], $configuration['search.']) : $configuration['search'];
5022  $replace = isset($configuration['replace.']) ? $this->stdWrap($configuration['replace'], $configuration['replace.']) : $configuration['replace'];
5023  // Determines whether regular expression shall be used
5024  if (isset($configuration['useRegExp']) || $configuration['useRegExp.']) {
5025  $useRegularExpression = isset($configuration['useRegExp.']) ? $this->stdWrap($configuration['useRegExp'], $configuration['useRegExp.']) : $configuration['useRegExp'];
5026  }
5027  // Determines whether replace-pattern uses option-split
5028  if (isset($configuration['useOptionSplitReplace']) || isset($configuration['useOptionSplitReplace.'])) {
5029  $useOptionSplitReplace = isset($configuration['useOptionSplitReplace.']) ? $this->stdWrap($configuration['useOptionSplitReplace'], $configuration['useOptionSplitReplace.']) : $configuration['useOptionSplitReplace'];
5030  }
5031 
5032  // Performs a replacement by preg_replace()
5033  if (isset($useRegularExpression)) {
5034  // Get separator-character which precedes the string and separates search-string from the modifiers
5035  $separator = $search[0];
5036  $startModifiers = strrpos($search, $separator);
5037  if ($separator !== false && $startModifiers > 0) {
5038  $modifiers = substr($search, $startModifiers + 1);
5039  // remove "e" (eval-modifier), which would otherwise allow to run arbitrary PHP-code
5040  $modifiers = str_replace('e', '', $modifiers);
5041  $search = substr($search, 0, ($startModifiers + 1)) . $modifiers;
5042  }
5043  if (empty($useOptionSplitReplace)) {
5044  $content = preg_replace($search, $replace, $content);
5045  } else {
5046  // init for replacement
5047  $splitCount = preg_match_all($search, $content, $matches);
5048  $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray([$replace], $splitCount);
5049  $replaceCount = 0;
5050 
5051  $replaceCallback = function ($match) use ($replaceArray, $search, &$replaceCount) {
5052  $replaceCount++;
5053  return preg_replace($search, $replaceArray[$replaceCount - 1][0], $match[0]);
5054  };
5055  $content = preg_replace_callback($search, $replaceCallback, $content);
5056  }
5057  } else {
5058  if (empty($useOptionSplitReplace)) {
5059  $content = str_replace($search, $replace, $content);
5060  } else {
5061  // turn search-string into a preg-pattern
5062  $searchPreg = '#' . preg_quote($search, '#') . '#';
5063 
5064  // init for replacement
5065  $splitCount = preg_match_all($searchPreg, $content, $matches);
5066  $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray([$replace], $splitCount);
5067  $replaceCount = 0;
5068 
5069  $replaceCallback = function () use ($replaceArray, $search, &$replaceCount) {
5070  $replaceCount++;
5071  return $replaceArray[$replaceCount - 1][0];
5072  };
5073  $content = preg_replace_callback($searchPreg, $replaceCallback, $content);
5074  }
5075  }
5076  }
5077  return $content;
5078  }
5079 
5088  protected function round($content, array $conf = [])
5089  {
5090  $decimals = isset($conf['decimals.']) ? $this->stdWrap($conf['decimals'], $conf['decimals.']) : $conf['decimals'];
5091  $type = isset($conf['roundType.']) ? $this->stdWrap($conf['roundType'], $conf['roundType.']) : $conf['roundType'];
5092  $floatVal = floatval($content);
5093  switch ($type) {
5094  case 'ceil':
5095  $content = ceil($floatVal);
5096  break;
5097  case 'floor':
5098  $content = floor($floatVal);
5099  break;
5100  case 'round':
5101 
5102  default:
5103  $content = round($floatVal, (int)$decimals);
5104  }
5105  return $content;
5106  }
5107 
5116  public function numberFormat($content, $conf)
5117  {
5118  $decimals = isset($conf['decimals.']) ? (int)$this->stdWrap($conf['decimals'], $conf['decimals.']) : (int)$conf['decimals'];
5119  $dec_point = isset($conf['dec_point.']) ? $this->stdWrap($conf['dec_point'], $conf['dec_point.']) : $conf['dec_point'];
5120  $thousands_sep = isset($conf['thousands_sep.']) ? $this->stdWrap($conf['thousands_sep'], $conf['thousands_sep.']) : $conf['thousands_sep'];
5121  return number_format(floatval($content), $decimals, $dec_point, $thousands_sep);
5122  }
5123 
5143  public function parseFunc($theValue, $conf, $ref = '')
5144  {
5145  // Fetch / merge reference, if any
5146  if ($ref) {
5147  $temp_conf = [
5148  'parseFunc' => $ref,
5149  'parseFunc.' => $conf
5150  ];
5151  $temp_conf = $this->mergeTSRef($temp_conf, 'parseFunc');
5152  $conf = $temp_conf['parseFunc.'];
5153  }
5154  // Process:
5155  if ((string)$conf['externalBlocks'] === '') {
5156  return $this->_parseFunc($theValue, $conf);
5157  }
5158  $tags = strtolower(implode(',', GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
5159  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
5160  $parts = $htmlParser->splitIntoBlock($tags, $theValue);
5161  foreach ($parts as $k => $v) {
5162  if ($k % 2) {
5163  // font:
5164  $tagName = strtolower($htmlParser->getFirstTagName($v));
5165  $cfg = $conf['externalBlocks.'][$tagName . '.'];
5166  if ($cfg['stripNLprev'] || $cfg['stripNL']) {
5167  $parts[$k - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $parts[$k - 1]);
5168  }
5169  if ($cfg['stripNLnext'] || $cfg['stripNL']) {
5170  $parts[$k + 1] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $parts[$k + 1]);
5171  }
5172  }
5173  }
5174  foreach ($parts as $k => $v) {
5175  if ($k % 2) {
5176  $tag = $htmlParser->getFirstTag($v);
5177  $tagName = strtolower($htmlParser->getFirstTagName($v));
5178  $cfg = $conf['externalBlocks.'][$tagName . '.'];
5179  if ($cfg['callRecursive']) {
5180  $parts[$k] = $this->parseFunc($htmlParser->removeFirstAndLastTag($v), $conf);
5181  if (!$cfg['callRecursive.']['dontWrapSelf']) {
5182  if ($cfg['callRecursive.']['alternativeWrap']) {
5183  $parts[$k] = $this->wrap($parts[$k], $cfg['callRecursive.']['alternativeWrap']);
5184  } else {
5185  if (is_array($cfg['callRecursive.']['tagStdWrap.'])) {
5186  $tag = $this->stdWrap($tag, $cfg['callRecursive.']['tagStdWrap.']);
5187  }
5188  $parts[$k] = $tag . $parts[$k] . '</' . $tagName . '>';
5189  }
5190  }
5191  } elseif ($cfg['HTMLtableCells']) {
5192  $rowParts = $htmlParser->splitIntoBlock('tr', $parts[$k]);
5193  foreach ($rowParts as $kk => $vv) {
5194  if ($kk % 2) {
5195  $colParts = $htmlParser->splitIntoBlock('td,th', $vv);
5196  $cc = 0;
5197  foreach ($colParts as $kkk => $vvv) {
5198  if ($kkk % 2) {
5199  $cc++;
5200  $tag = $htmlParser->getFirstTag($vvv);
5201  $tagName = strtolower($htmlParser->getFirstTagName($vvv));
5202  $colParts[$kkk] = $htmlParser->removeFirstAndLastTag($vvv);
5203  if ($cfg['HTMLtableCells.'][$cc . '.']['callRecursive'] || !isset($cfg['HTMLtableCells.'][$cc . '.']['callRecursive']) && $cfg['HTMLtableCells.']['default.']['callRecursive']) {
5204  if ($cfg['HTMLtableCells.']['addChr10BetweenParagraphs']) {
5205  $colParts[$kkk] = str_replace('</p><p>', '</p>' . LF . '<p>', $colParts[$kkk]);
5206  }
5207  $colParts[$kkk] = $this->parseFunc($colParts[$kkk], $conf);
5208  }
5209  $tagStdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.'])
5210  ? $cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.']
5211  : $cfg['HTMLtableCells.']['default.']['tagStdWrap.'];
5212  if (is_array($tagStdWrap)) {
5213  $tag = $this->stdWrap($tag, $tagStdWrap);
5214  }
5215  $stdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['stdWrap.'])
5216  ? $cfg['HTMLtableCells.'][$cc . '.']['stdWrap.']
5217  : $cfg['HTMLtableCells.']['default.']['stdWrap.'];
5218  if (is_array($stdWrap)) {
5219  $colParts[$kkk] = $this->stdWrap($colParts[$kkk], $stdWrap);
5220  }
5221  $colParts[$kkk] = $tag . $colParts[$kkk] . '</' . $tagName . '>';
5222  }
5223  }
5224  $rowParts[$kk] = implode('', $colParts);
5225  }
5226  }
5227  $parts[$k] = implode('', $rowParts);
5228  }
5229  if (is_array($cfg['stdWrap.'])) {
5230  $parts[$k] = $this->stdWrap($parts[$k], $cfg['stdWrap.']);
5231  }
5232  } else {
5233  $parts[$k] = $this->_parseFunc($parts[$k], $conf);
5234  }
5235  }
5236  return implode('', $parts);
5237  }
5238 
5248  public function _parseFunc($theValue, $conf)
5249  {
5250  if (!empty($conf['if.']) && !$this->checkIf($conf['if.'])) {
5251  return $theValue;
5252  }
5253  // Indicates that the data is from within a tag.
5254  $inside = 0;
5255  // Pointer to the total string position
5256  $pointer = 0;
5257  // Loaded with the current typo-tag if any.
5258  $currentTag = '';
5259  $stripNL = 0;
5260  $contentAccum = [];
5261  $contentAccumP = 0;
5262  $allowTags = strtolower(str_replace(' ', '', $conf['allowTags']));
5263  $denyTags = strtolower(str_replace(' ', '', $conf['denyTags']));
5264  $totalLen = strlen($theValue);
5265  do {
5266  if (!$inside) {
5267  if (!is_array($currentTag)) {
5268  // These operations should only be performed on code outside the typotags...
5269  // data: this checks that we enter tags ONLY if the first char in the tag is alphanumeric OR '/'
5270  $len_p = 0;
5271  $c = 100;
5272  do {
5273  $len = strcspn(substr($theValue, $pointer + $len_p), '<');
5274  $len_p += $len + 1;
5275  $endChar = ord(strtolower(substr($theValue, $pointer + $len_p, 1)));
5276  $c--;
5277  } while ($c > 0 && $endChar && ($endChar < 97 || $endChar > 122) && $endChar != 47);
5278  $len = $len_p - 1;
5279  } else {
5280  // If we're inside a currentTag, just take it to the end of that tag!
5281  $tempContent = strtolower(substr($theValue, $pointer));
5282  $len = strpos($tempContent, '</' . $currentTag[0]);
5283  if (is_string($len) && !$len) {
5284  $len = strlen($tempContent);
5285  }
5286  }
5287  // $data is the content until the next <tag-start or end is detected.
5288  // In case of a currentTag set, this would mean all data between the start- and end-tags
5289  $data = substr($theValue, $pointer, $len);
5290  if ($data != '') {
5291  if ($stripNL) {
5292  // If the previous tag was set to strip NewLines in the beginning of the next data-chunk.
5293  $data = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $data);
5294  }
5295  // These operations should only be performed on code outside the tags...
5296  if (!is_array($currentTag)) {
5297  // Constants
5298  $tsfe = $this->getTypoScriptFrontendController();
5299  $tmpConstants = $tsfe->tmpl->setup['constants.'];
5300  if ($conf['constants'] && is_array($tmpConstants)) {
5301  foreach ($tmpConstants as $key => $val) {
5302  if (is_string($val)) {
5303  $data = str_replace('###' . $key . '###', $val, $data);
5304  }
5305  }
5306  }
5307  // Short
5308  if (is_array($conf['short.'])) {
5309  $shortWords = $conf['short.'];
5310  krsort($shortWords);
5311  foreach ($shortWords as $key => $val) {
5312  if (is_string($val)) {
5313  $data = str_replace($key, $val, $data);
5314  }
5315  }
5316  }
5317  // stdWrap
5318  if (is_array($conf['plainTextStdWrap.'])) {
5319  $data = $this->stdWrap($data, $conf['plainTextStdWrap.']);
5320  }
5321  // userFunc
5322  if ($conf['userFunc']) {
5323  $data = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $data);
5324  }
5325  // Makelinks: (Before search-words as we need the links to be generated when searchwords go on...!)
5326  if ($conf['makelinks']) {
5327  $data = $this->http_makelinks($data, $conf['makelinks.']['http.']);
5328  $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.']);
5329  }
5330  // Search Words:
5331  if ($tsfe->no_cache && $conf['sword'] && is_array($tsfe->sWordList) && $tsfe->sWordRegEx) {
5332  $newstring = '';
5333  do {
5334  $pregSplitMode = 'i';
5335  if (isset($tsfe->config['config']['sword_noMixedCase']) && !empty($tsfe->config['config']['sword_noMixedCase'])) {
5336  $pregSplitMode = '';
5337  }
5338  $pieces = preg_split('/' . $tsfe->sWordRegEx . '/' . $pregSplitMode, $data, 2);
5339  $newstring .= $pieces[0];
5340  $match_len = strlen($data) - (strlen($pieces[0]) + strlen($pieces[1]));
5341  $inTag = false;
5342  if (strstr($pieces[0], '<') || strstr($pieces[0], '>')) {
5343  // Returns TRUE, if a '<' is closer to the string-end than '>'.
5344  // This is the case if we're INSIDE a tag (that could have been
5345  // made by makelinks...) and we must secure, that the inside of a tag is
5346  // not marked up.
5347  $inTag = strrpos($pieces[0], '<') > strrpos($pieces[0], '>');
5348  }
5349  // The searchword:
5350  $match = substr($data, strlen($pieces[0]), $match_len);
5351  if (trim($match) && strlen($match) > 1 && !$inTag) {
5352  $match = $this->wrap($match, $conf['sword']);
5353  }
5354  // Concatenate the Search Word again.
5355  $newstring .= $match;
5356  $data = $pieces[1];
5357  } while ($pieces[1]);
5358  $data = $newstring;
5359  }
5360  }
5361  $contentAccum[$contentAccumP] .= $data;
5362  }
5363  $inside = 1;
5364  } else {
5365  // tags
5366  $len = strcspn(substr($theValue, $pointer), '>') + 1;
5367  $data = substr($theValue, $pointer, $len);
5368  if (StringUtility::endsWith($data, '/>') && !StringUtility::beginsWith($data, '<link ')) {
5369  $tagContent = substr($data, 1, -2);
5370  } else {
5371  $tagContent = substr($data, 1, -1);
5372  }
5373  $tag = explode(' ', trim($tagContent), 2);
5374  $tag[0] = strtolower($tag[0]);
5375  if ($tag[0][0] === '/') {
5376  $tag[0] = substr($tag[0], 1);
5377  $tag['out'] = 1;
5378  }
5379  if ($conf['tags.'][$tag[0]]) {
5380  $treated = false;
5381  $stripNL = false;
5382  // in-tag
5383  if (!$currentTag && !$tag['out']) {
5384  // $currentTag (array!) is the tag we are currently processing
5385  $currentTag = $tag;
5386  $contentAccumP++;
5387  $treated = true;
5388  // in-out-tag: img and other empty tags
5389  if (preg_match('/^(area|base|br|col|hr|img|input|meta|param)$/i', $tag[0])) {
5390  $tag['out'] = 1;
5391  }
5392  }
5393  // out-tag
5394  if ($currentTag[0] === $tag[0] && $tag['out']) {
5395  $theName = $conf['tags.'][$tag[0]];
5396  $theConf = $conf['tags.'][$tag[0] . '.'];
5397  // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
5398  $stripNL = (bool)$theConf['stripNL'];
5399  // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
5400  $breakOut = $theConf['breakoutTypoTagContent'] ? 1 : 0;
5401  $this->parameters = [];
5402  if ($currentTag[1]) {
5403  $params = GeneralUtility::get_tag_attributes($currentTag[1]);
5404  if (is_array($params)) {
5405  foreach ($params as $option => $val) {
5406  $this->parameters[strtolower($option)] = $val;
5407  }
5408  }
5409  }
5410  $this->parameters['allParams'] = trim($currentTag[1]);
5411  // Removes NL in the beginning and end of the tag-content AND at the end of the currentTagBuffer.
5412  // $stripNL depends on the configuration of the current tag
5413  if ($stripNL) {
5414  $contentAccum[$contentAccumP - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP - 1]);
5415  $contentAccum[$contentAccumP] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $contentAccum[$contentAccumP]);
5416  $contentAccum[$contentAccumP] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP]);
5417  }
5418  $this->data[$this->currentValKey] = $contentAccum[$contentAccumP];
5419  $newInput = $this->cObjGetSingle($theName, $theConf, '/parseFunc/.tags.' . $tag[0]);
5420  // fetch the content object
5421  $contentAccum[$contentAccumP] = $newInput;
5422  $contentAccumP++;
5423  // If the TypoTag section
5424  if (!$breakOut) {
5425  $contentAccum[$contentAccumP - 2] .= $contentAccum[$contentAccumP - 1] . $contentAccum[$contentAccumP];
5426  unset($contentAccum[$contentAccumP]);
5427  unset($contentAccum[$contentAccumP - 1]);
5428  $contentAccumP -= 2;
5429  }
5430  unset($currentTag);
5431  $treated = true;
5432  }
5433  // other tags
5434  if (!$treated) {
5435  $contentAccum[$contentAccumP] .= $data;
5436  }
5437  } else {
5438  // If a tag was not a typo tag, then it is just added to the content
5439  $stripNL = false;
5440  if (GeneralUtility::inList($allowTags, $tag[0]) || $denyTags != '*' && !GeneralUtility::inList($denyTags, $tag[0])) {
5441  $contentAccum[$contentAccumP] .= $data;
5442  } else {
5443  $contentAccum[$contentAccumP] .= htmlspecialchars($data);
5444  }
5445  }
5446  $inside = 0;
5447  }
5448  $pointer += $len;
5449  } while ($pointer < $totalLen);
5450  // Parsing nonTypoTag content (all even keys):
5451  reset($contentAccum);
5452  $contentAccumCount = count($contentAccum);
5453  for ($a = 0; $a < $contentAccumCount; $a++) {
5454  if ($a % 2 != 1) {
5455  // stdWrap
5456  if (is_array($conf['nonTypoTagStdWrap.'])) {
5457  $contentAccum[$a] = $this->stdWrap($contentAccum[$a], $conf['nonTypoTagStdWrap.']);
5458  }
5459  // userFunc
5460  if ($conf['nonTypoTagUserFunc']) {
5461  $contentAccum[$a] = $this->callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'], $contentAccum[$a]);
5462  }
5463  }
5464  }
5465  return implode('', $contentAccum);
5466  }
5467 
5476  public function encaps_lineSplit($theValue, $conf)
5477  {
5478  $lParts = explode(LF, $theValue);
5479  $encapTags = GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList']), true);
5480  $nonWrappedTag = $conf['nonWrappedTag'];
5481  $defaultAlign = isset($conf['defaultAlign.'])
5482  ? trim($this->stdWrap($conf['defaultAlign'], $conf['defaultAlign.']))
5483  : trim($conf['defaultAlign']);
5484  if ((string)$theValue === '') {
5485  return '';
5486  }
5487  $str_content = '';
5488  foreach ($lParts as $k => $l) {
5489  $sameBeginEnd = 0;
5490  $emptyTag = 0;
5491  $l = trim($l);
5492  $attrib = [];
5493  $nWrapped = 0;
5494  $tagName = '';
5495  if ($l[0] === '<' && substr($l, -1) === '>') {
5496  $fwParts = explode('>', substr($l, 1), 2);
5497  list($tagName) = explode(' ', $fwParts[0], 2);
5498  if (!$fwParts[1]) {
5499  if (substr($tagName, -1) === '/') {
5500  $tagName = substr($tagName, 0, -1);
5501  }
5502  if (substr($fwParts[0], -1) === '/') {
5503  $sameBeginEnd = 1;
5504  $emptyTag = 1;
5505  $attrib = GeneralUtility::get_tag_attributes('<' . substr($fwParts[0], 0, -1) . '>');
5506  }
5507  } else {
5508  $backParts = GeneralUtility::revExplode('<', substr($fwParts[1], 0, -1), 2);
5509  $attrib = GeneralUtility::get_tag_attributes('<' . $fwParts[0] . '>');
5510  $str_content = $backParts[0];
5511  $sameBeginEnd = substr(strtolower($backParts[1]), 1, strlen($tagName)) === strtolower($tagName);
5512  }
5513  }
5514  if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
5515  $uTagName = strtoupper($tagName);
5516  $uTagName = strtoupper($conf['remapTag.'][$uTagName] ? $conf['remapTag.'][$uTagName] : $uTagName);
5517  } else {
5518  $uTagName = strtoupper($nonWrappedTag);
5519  // The line will be wrapped: $uTagName should not be an empty tag
5520  $emptyTag = 0;
5521  $str_content = $lParts[$k];
5522  $nWrapped = 1;
5523  $attrib = [];
5524  }
5525  // Wrapping all inner-content:
5526  if (is_array($conf['innerStdWrap_all.'])) {
5527  $str_content = $this->stdWrap($str_content, $conf['innerStdWrap_all.']);
5528  }
5529  if ($uTagName) {
5530  // Setting common attributes
5531  if (is_array($conf['addAttributes.'][$uTagName . '.'])) {
5532  foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
5533  if (!is_array($vv)) {
5534  if ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'blank') {
5535  if ((string)$attrib[$kk] === '') {
5536  $attrib[$kk] = $vv;
5537  }
5538  } elseif ((string)$conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] === 'exists') {
5539  if (!isset($attrib[$kk])) {
5540  $attrib[$kk] = $vv;
5541  }
5542  } else {
5543  $attrib[$kk] = $vv;
5544  }
5545  }
5546  }
5547  }
5548  // Wrapping all inner-content:
5549  if (is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
5550  $str_content = $this->stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
5551  }
5552  // Default align
5553  if (!$attrib['align'] && $defaultAlign) {
5554  $attrib['align'] = $defaultAlign;
5555  }
5557  if (!($conf['removeWrapping'] && !($emptyTag && $conf['removeWrapping.']['keepSingleTag']))) {
5558  if ($emptyTag) {
5559  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
5560  } else {
5561  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
5562  }
5563  }
5564  }
5565  if ($nWrapped && $conf['wrapNonWrappedLines']) {
5566  $str_content = $this->wrap($str_content, $conf['wrapNonWrappedLines']);
5567  }
5568  $lParts[$k] = $str_content;
5569  }
5570  return implode(LF, $lParts);
5571  }
5572 
5583  public function http_makelinks($data, $conf)
5584  {
5585  $aTagParams = $this->getATagParams($conf);
5586  $textstr = '';
5587  foreach ([ 'http://', 'https://' ] as $scheme) {
5588  $textpieces = explode($scheme, $data);
5589  $pieces = count($textpieces);
5590  $textstr = $textpieces[0];
5591  for ($i = 1; $i < $pieces; $i++) {
5592  $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
5593  if (trim(substr($textstr, -1)) === '' && $len) {
5594  $lastChar = substr($textpieces[$i], $len - 1, 1);
5595  if (!preg_match('/[A-Za-z0-9\\/#_-]/', $lastChar)) {
5596  $len--;
5597  }
5598  // Included '\/' 3/12
5599  $parts[0] = substr($textpieces[$i], 0, $len);
5600  $parts[1] = substr($textpieces[$i], $len);
5601  $keep = $conf['keep'];
5602  $linkParts = parse_url($scheme . $parts[0]);
5603  $linktxt = '';
5604  if (strstr($keep, 'scheme')) {
5605  $linktxt = $scheme;
5606  }
5607  $linktxt .= $linkParts['host'];
5608  if (strstr($keep, 'path')) {
5609  $linktxt .= $linkParts['path'];
5610  // Added $linkParts['query'] 3/12
5611  if (strstr($keep, 'query') && $linkParts['query']) {
5612  $linktxt .= '?' . $linkParts['query'];
5613  } elseif ($linkParts['path'] === '/') {
5614  $linktxt = substr($linktxt, 0, -1);
5615  }
5616  }
5617  if (isset($conf['extTarget'])) {
5618  if (isset($conf['extTarget.'])) {
5619  $target = $this->stdWrap($conf['extTarget'], $conf['extTarget.']);
5620  } else {
5621  $target = $conf['extTarget'];
5622  }
5623  } else {
5624  $target = $this->getTypoScriptFrontendController()->extTarget;
5625  }
5626 
5627  // check for jump URLs or similar
5628  $linkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_COMMON, $scheme . $parts[0], $conf);
5629 
5630  $res = '<a href="' . htmlspecialchars($linkUrl) . '"'
5631  . ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
5632  . $aTagParams . $this->extLinkATagParams(('http://' . $parts[0]), 'url') . '>';
5633 
5634  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
5635  if ((string)$conf['ATagBeforeWrap'] !== '') {
5636  $res = $res . $this->wrap($linktxt, $wrap) . '</a>';
5637  } else {
5638  $res = $this->wrap($res . $linktxt . '</a>', $wrap);
5639  }
5640  $textstr .= $res . $parts[1];
5641  } else {
5642  $textstr .= $scheme . $textpieces[$i];
5643  }
5644  }
5645  $data = $textstr;
5646  }
5647  return $textstr;
5648  }
5649 
5659  public function mailto_makelinks($data, $conf)
5660  {
5661  // http-split
5662  $aTagParams = $this->getATagParams($conf);
5663  $textpieces = explode('mailto:', $data);
5664  $pieces = count($textpieces);
5665  $textstr = $textpieces[0];
5666  $tsfe = $this->getTypoScriptFrontendController();
5667  for ($i = 1; $i < $pieces; $i++) {
5668  $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
5669  if (trim(substr($textstr, -1)) === '' && $len) {
5670  $lastChar = substr($textpieces[$i], $len - 1, 1);
5671  if (!preg_match('/[A-Za-z0-9]/', $lastChar)) {
5672  $len--;
5673  }
5674  $parts[0] = substr($textpieces[$i], 0, $len);
5675  $parts[1] = substr($textpieces[$i], $len);
5676  $linktxt = preg_replace('/\\?.*/', '', $parts[0]);
5677  list($mailToUrl, $linktxt) = $this->getMailTo($parts[0], $linktxt);
5678  $mailToUrl = $tsfe->spamProtectEmailAddresses === 'ascii' ? $mailToUrl : htmlspecialchars($mailToUrl);
5679  $res = '<a href="' . $mailToUrl . '"' . $aTagParams . '>';
5680  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
5681  if ((string)$conf['ATagBeforeWrap'] !== '') {
5682  $res = $res . $this->wrap($linktxt, $wrap) . '</a>';
5683  } else {
5684  $res = $this->wrap($res . $linktxt . '</a>', $wrap);
5685  }
5686  $textstr .= $res . $parts[1];
5687  } else {
5688  $textstr .= 'mailto:' . $textpieces[$i];
5689  }
5690  }
5691  return $textstr;
5692  }
5693 
5718  public function getImgResource($file, $fileArray)
5719  {
5720  if (empty($file) && empty($fileArray)) {
5721  return null;
5722  }
5723  if (!is_array($fileArray)) {
5724  $fileArray = (array)$fileArray;
5725  }
5726  $imageResource = null;
5727  $tsfe = $this->getTypoScriptFrontendController();
5728  if ($file === 'GIFBUILDER') {
5730  $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
5731  $gifCreator->init();
5732  $theImage = '';
5733  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
5734  $gifCreator->start($fileArray, $this->data);
5735  $theImage = $gifCreator->gifBuild();
5736  }
5737  $imageResource = $gifCreator->getImageDimensions($theImage);
5738  $imageResource['origFile'] = $theImage;
5739  } else {
5740  if ($file instanceof File) {
5741  $fileObject = $file;
5742  } elseif ($file instanceof FileReference) {
5743  $fileObject = $file->getOriginalFile();
5744  if (!isset($fileArray['crop'])) {
5745  $fileArray['crop'] = $file->getProperty('crop');
5746  }
5747  } else {
5748  try {
5749  if ($fileArray['import.']) {
5750  $importedFile = trim($this->stdWrap('', $fileArray['import.']));
5751  if (!empty($importedFile)) {
5752  $file = $importedFile;
5753  }
5754  }
5755 
5757  $treatIdAsReference = isset($fileArray['treatIdAsReference.']) ? $this->stdWrap($fileArray['treatIdAsReference'], $fileArray['treatIdAsReference.']) : $fileArray['treatIdAsReference'];
5758  if (!empty($treatIdAsReference)) {
5759  $fileReference = $this->getResourceFactory()->getFileReferenceObject($file);
5760  $fileObject = $fileReference->getOriginalFile();
5761  if (!isset($fileArray['crop'])) {
5762  $fileArray['crop'] = $fileReference->getProperty('crop');
5763  }
5764  } else {
5765  $fileObject = $this->getResourceFactory()->getFileObject($file);
5766  }
5767  } elseif (preg_match('/^(0|[1-9][0-9]*):/', $file)) { // combined identifier
5768  $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
5769  } else {
5770  if (isset($importedFile) && !empty($importedFile) && !empty($fileArray['import'])) {
5771  $file = $fileArray['import'] . $file;
5772  }
5773  $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
5774  }
5775  } catch (Exception $exception) {
5777  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
5778  $logger->warning('The image "' . $file . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
5779  return null;
5780  }
5781  }
5782  if ($fileObject instanceof File) {
5783  $processingConfiguration = [];
5784  $processingConfiguration['width'] = isset($fileArray['width.']) ? $this->stdWrap($fileArray['width'], $fileArray['width.']) : $fileArray['width'];
5785  $processingConfiguration['height'] = isset($fileArray['height.']) ? $this->stdWrap($fileArray['height'], $fileArray['height.']) : $fileArray['height'];
5786  $processingConfiguration['fileExtension'] = isset($fileArray['ext.']) ? $this->stdWrap($fileArray['ext'], $fileArray['ext.']) : $fileArray['ext'];
5787  $processingConfiguration['maxWidth'] = isset($fileArray['maxW.']) ? (int)$this->stdWrap($fileArray['maxW'], $fileArray['maxW.']) : (int)$fileArray['maxW'];
5788  $processingConfiguration['maxHeight'] = isset($fileArray['maxH.']) ? (int)$this->stdWrap($fileArray['maxH'], $fileArray['maxH.']) : (int)$fileArray['maxH'];
5789  $processingConfiguration['minWidth'] = isset($fileArray['minW.']) ? (int)$this->stdWrap($fileArray['minW'], $fileArray['minW.']) : (int)$fileArray['minW'];
5790  $processingConfiguration['minHeight'] = isset($fileArray['minH.']) ? (int)$this->stdWrap($fileArray['minH'], $fileArray['minH.']) : (int)$fileArray['minH'];
5791  $processingConfiguration['noScale'] = isset($fileArray['noScale.']) ? $this->stdWrap($fileArray['noScale'], $fileArray['noScale.']) : $fileArray['noScale'];
5792  $processingConfiguration['additionalParameters'] = isset($fileArray['params.']) ? $this->stdWrap($fileArray['params'], $fileArray['params.']) : $fileArray['params'];
5793  $processingConfiguration['frame'] = isset($fileArray['frame.']) ? (int)$this->stdWrap($fileArray['frame'], $fileArray['frame.']) : (int)$fileArray['frame'];
5794  $processingConfiguration['crop'] = isset($fileArray['crop.'])
5795  ? $this->stdWrap($fileArray['crop'], $fileArray['crop.'])
5796  : (isset($fileArray['crop']) ? $fileArray['crop'] : null);
5797  // Possibility to cancel/force profile extraction
5798  // see $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_stripProfileCommand']
5799  if (isset($fileArray['stripProfile'])) {
5800  $processingConfiguration['stripProfile'] = $fileArray['stripProfile'];
5801  }
5802  // Check if we can handle this type of file for editing
5803  if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
5804  $maskArray = $fileArray['m.'];
5805  // Must render mask images and include in hash-calculating
5806  // - otherwise we cannot be sure the filename is unique for the setup!
5807  if (is_array($maskArray)) {
5808  $mask = $this->getImgResource($maskArray['mask'], $maskArray['mask.']);
5809  $bgImg = $this->getImgResource($maskArray['bgImg'], $maskArray['bgImg.']);
5810  $bottomImg = $this->getImgResource($maskArray['bottomImg'], $maskArray['bottomImg.']);
5811  $bottomImg_mask = $this->getImgResource($maskArray['bottomImg_mask'], $maskArray['bottomImg_mask.']);
5812 
5813  $processingConfiguration['maskImages']['maskImage'] = $mask['processedFile'];
5814  $processingConfiguration['maskImages']['backgroundImage'] = $bgImg['processedFile'];
5815  $processingConfiguration['maskImages']['maskBottomImage'] = $bottomImg['processedFile'];
5816  $processingConfiguration['maskImages']['maskBottomImageMask'] = $bottomImg_mask['processedFile'];
5817  }
5818  $processedFileObject = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
5819  $hash = $processedFileObject->calculateChecksum();
5820  // store info in the TSFE template cache (kept for backwards compatibility)
5821  if ($processedFileObject->isProcessed() && !isset($tsfe->tmpl->fileCache[$hash])) {
5822  $tsfe->tmpl->fileCache[$hash] = [
5823  0 => $processedFileObject->getProperty('width'),
5824  1 => $processedFileObject->getProperty('height'),
5825  2 => $processedFileObject->getExtension(),
5826  3 => $processedFileObject->getPublicUrl(),
5827  'origFile' => $fileObject->getPublicUrl(),
5828  'origFile_mtime' => $fileObject->getModificationTime(),
5829  // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder,
5830  // in order for the setup-array to create a unique filename hash.
5831  'originalFile' => $fileObject,
5832  'processedFile' => $processedFileObject,
5833  'fileCacheHash' => $hash
5834  ];
5835  }
5836  $imageResource = $tsfe->tmpl->fileCache[$hash];
5837  }
5838  }
5839  }
5840  // If image was processed by GIFBUILDER:
5841  // ($imageResource indicates that it was processed the regular way)
5842  if (!isset($imageResource)) {
5843  $theImage = $tsfe->tmpl->getFileName($file);
5844  if ($theImage) {
5845  $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
5847  $gifCreator->init();
5848  $info = $gifCreator->imageMagickConvert($theImage, 'WEB');
5849  $info['origFile'] = $theImage;
5850  // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder, ln 100ff in order for the setup-array to create a unique filename hash.
5851  $info['origFile_mtime'] = @filemtime($theImage);
5852  $imageResource = $info;
5853  }
5854  }
5855  // Hook 'getImgResource': Post-processing of image resources
5856  if (isset($imageResource)) {
5858  foreach ($this->getGetImgResourceHookObjects() as $hookObject) {
5859  $imageResource = $hookObject->getImgResourcePostProcess($file, (array)$fileArray, $imageResource, $this);
5860  }
5861  }
5862  return $imageResource;
5863  }
5864 
5865  /***********************************************
5866  *
5867  * Data retrieval etc.
5868  *
5869  ***********************************************/
5876  public function getFieldVal($field)
5877  {
5878  if (!strstr($field, '//')) {
5879  return $this->data[trim($field)];
5880  } else {
5881  $sections = GeneralUtility::trimExplode('//', $field, true);
5882  foreach ($sections as $k) {
5883  if ((string)$this->data[$k] !== '') {
5884  return $this->data[$k];
5885  }
5886  }
5887  }
5888  return '';
5889  }
5890 
5899  public function getData($string, $fieldArray = null)
5900  {
5901  $tsfe = $this->getTypoScriptFrontendController();
5902  if (!is_array($fieldArray)) {
5903  $fieldArray = $tsfe->page;
5904  }
5905  $retVal = '';
5906  $sections = explode('//', $string);
5907  foreach ($sections as $secKey => $secVal) {
5908  if ($retVal) {
5909  break;
5910  }
5911  $parts = explode(':', $secVal, 2);
5912  $type = strtolower(trim($parts[0]));
5913  $typesWithOutParameters = ['level', 'date', 'current', 'pagelayout'];
5914  $key = trim($parts[1]);
5915  if (($key != '') || in_array($type, $typesWithOutParameters)) {
5916  switch ($type) {
5917  case 'gp':
5918  // Merge GET and POST and get $key out of the merged array
5919  $getPostArray = GeneralUtility::_GET();
5921  $retVal = $this->getGlobal($key, $getPostArray);
5922  break;
5923  case 'tsfe':
5924  $retVal = $this->getGlobal('TSFE|' . $key);
5925  break;
5926  case 'getenv':
5927  $retVal = getenv($key);
5928  break;
5929  case 'getindpenv':
5930  $retVal = $this->getEnvironmentVariable($key);
5931  break;
5932  case 'field':
5933  $retVal = $this->getGlobal($key, $fieldArray);
5934  break;
5935  case 'file':
5936  $retVal = $this->getFileDataKey($key);
5937  break;
5938  case 'parameters':
5939  $retVal = $this->parameters[$key];
5940  break;
5941  case 'register':
5942  $retVal = $tsfe->register[$key];
5943  break;
5944  case 'global':
5945  $retVal = $this->getGlobal($key);
5946  break;
5947  case 'level':
5948  $retVal = count($tsfe->tmpl->rootLine) - 1;
5949  break;
5950  case 'leveltitle':
5951  $keyParts = GeneralUtility::trimExplode(',', $key);
5952  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5953  $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1]) === 'slide');
5954  break;
5955  case 'levelmedia':
5956  $keyParts = GeneralUtility::trimExplode(',', $key);
5957  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5958  $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1]) === 'slide');
5959  break;
5960  case 'leveluid':
5961  $numericKey = $this->getKey($key, $tsfe->tmpl->rootLine);
5962  $retVal = $this->rootLineValue($numericKey, 'uid');
5963  break;
5964  case 'levelfield':
5965  $keyParts = GeneralUtility::trimExplode(',', $key);
5966  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5967  $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2]) === 'slide');
5968  break;
5969  case 'fullrootline':
5970  $keyParts = GeneralUtility::trimExplode(',', $key);
5971  $fullKey = (int)$keyParts[0] - count($tsfe->tmpl->rootLine) + count($tsfe->rootLine);
5972  if ($fullKey >= 0) {
5973  $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2], 'slide'), $tsfe->rootLine);
5974  }
5975  break;
5976  case 'date':
5977  if (!$key) {
5978  $key = 'd/m Y';
5979  }
5980  $retVal = date($key, $GLOBALS['EXEC_TIME']);
5981  break;
5982  case 'page':
5983  $retVal = $tsfe->page[$key];
5984  break;
5985  case 'pagelayout':
5986  // Check if the current page has a value in the DB field "backend_layout"
5987  // if empty, check the root line for "backend_layout_next_level"
5988  // same as
5989  // field = backend_layout
5990  // ifEmpty.data = levelfield:-2, backend_layout_next_level, slide
5991  // ifEmpty.ifEmpty = default
5992  $retVal = $GLOBALS['TSFE']->page['backend_layout'];
5993 
5994  // If it is set to "none" - don't use any
5995  if ($retVal === '-1') {
5996  $retVal = 'none';
5997  } elseif ($retVal === '' || $retVal === '0') {
5998  // If it not set check the root-line for a layout on next level and use this
5999  // Remove first element, which is the current page
6000  // See also \TYPO3\CMS\Backend\View\BackendLayoutView::getSelectedCombinedIdentifier()
6001  $rootLine = $tsfe->rootLine;
6002  array_shift($rootLine);
6003  foreach ($rootLine as $rootLinePage) {
6004  $retVal = (string) $rootLinePage['backend_layout_next_level'];
6005  // If layout for "next level" is set to "none" - don't use any and stop searching
6006  if ($retVal === '-1') {
6007  $retVal = 'none';
6008  break;
6009  } elseif ($retVal !== '' && $retVal !== '0') {
6010  // Stop searching if a layout for "next level" is set
6011  break;
6012  }
6013  }
6014  }
6015  if ($retVal === '0' || $retVal === '') {
6016  $retVal = 'default';
6017  }
6018  break;
6019  case 'current':
6020  $retVal = $this->data[$this->currentValKey];
6021  break;
6022  case 'db':
6023  $selectParts = GeneralUtility::trimExplode(':', $key);
6024  $db_rec = $tsfe->sys_page->getRawRecord($selectParts[0], $selectParts[1]);
6025  if (is_array($db_rec) && $selectParts[2]) {
6026  $retVal = $db_rec[$selectParts[2]];
6027  }
6028  break;
6029  case 'lll':
6030  $retVal = $tsfe->sL('LLL:' . $key);
6031  break;
6032  case 'path':
6033  $retVal = $tsfe->tmpl->getFileName($key);
6034  break;
6035  case 'cobj':
6036  switch ($key) {
6037  case 'parentRecordNumber':
6038  $retVal = $this->parentRecordNumber;
6039  break;
6040  }
6041  break;
6042  case 'debug':
6043  switch ($key) {
6044  case 'rootLine':
6045  $retVal = DebugUtility::viewArray($tsfe->tmpl->rootLine);
6046  break;
6047  case 'fullRootLine':
6048  $retVal = DebugUtility::viewArray($tsfe->rootLine);
6049  break;
6050  case 'data':
6051  $retVal = DebugUtility::viewArray($this->data);
6052  break;
6053  case 'register':
6054  $retVal = DebugUtility::viewArray($tsfe->register);
6055  break;
6056  case 'page':
6057  $retVal = DebugUtility::viewArray($tsfe->page);
6058  break;
6059  }
6060  break;
6061  }
6062  }
6063  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'])) {
6064  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'] as $classData) {
6065  $hookObject = GeneralUtility::getUserObj($classData);
6066  if (!$hookObject instanceof ContentObjectGetDataHookInterface) {
6067  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetDataHookInterface::class, 1195044480);
6068  }
6069  $retVal = $hookObject->getDataExtension($string, $fieldArray, $secVal, $retVal, $this);
6070  }
6071  }
6072  }
6073  return $retVal;
6074  }
6075 
6085  protected function getFileDataKey($key)
6086  {
6087  list($fileUidOrCurrentKeyword, $requestedFileInformationKey) = explode(':', $key, 3);
6088  try {
6089  if ($fileUidOrCurrentKeyword === 'current') {
6090  $fileObject = $this->getCurrentFile();
6091  } elseif (MathUtility::canBeInterpretedAsInteger($fileUidOrCurrentKeyword)) {
6093  $fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
6094  $fileObject = $fileFactory->getFileObject($fileUidOrCurrentKeyword);
6095  } else {
6096  $fileObject = null;
6097  }
6098  } catch (Exception $exception) {
6100  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
6101  $logger->warning('The file "' . $fileUidOrCurrentKeyword . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]);
6102  $fileObject = null;
6103  }
6104 
6105  if ($fileObject instanceof FileInterface) {
6106  // All properties of the \TYPO3\CMS\Core\Resource\FileInterface are available here:
6107  switch ($requestedFileInformationKey) {
6108  case 'name':
6109  return $fileObject->getName();
6110  case 'uid':
6111  if (method_exists($fileObject, 'getUid')) {
6112  return $fileObject->getUid();
6113  }
6114  return 0;
6115  case 'originalUid':
6116  if ($fileObject instanceof FileReference) {
6117  return $fileObject->getOriginalFile()->getUid();
6118  }
6119  return null;
6120  case 'size':
6121  return $fileObject->getSize();
6122  case 'sha1':
6123  return $fileObject->getSha1();
6124  case 'extension':
6125  return $fileObject->getExtension();
6126  case 'mimetype':
6127  return $fileObject->getMimeType();
6128  case 'contents':
6129  return $fileObject->getContents();
6130  case 'publicUrl':
6131  return $fileObject->getPublicUrl();
6132  default:
6133  // Generic alternative here
6134  return $fileObject->getProperty($requestedFileInformationKey);
6135  }
6136  } else {
6137  // @todo fail silently as is common in tslib_content
6138  return 'Error: no file object';
6139  }
6140  }
6141 
6153  public function rootLineValue($key, $field, $slideBack = false, $altRootLine = '')
6154  {
6155  $rootLine = is_array($altRootLine) ? $altRootLine : $this->getTypoScriptFrontendController()->tmpl->rootLine;
6156  if (!$slideBack) {
6157  return $rootLine[$key][$field];
6158  } else {
6159  for ($a = $key; $a >= 0; $a--) {
6160  $val = $rootLine[$a][$field];
6161  if ($val) {
6162  return $val;
6163  }
6164  }
6165  }
6166  return '';
6167  }
6168 
6178  public function getGlobal($keyString, $source = null)
6179  {
6180  $keys = explode('|', $keyString);
6181  $numberOfLevels = count($keys);
6182  $rootKey = trim($keys[0]);
6183  $value = isset($source) ? $source[$rootKey] : $GLOBALS[$rootKey];
6184  for ($i = 1; $i < $numberOfLevels && isset($value); $i++) {
6185  $currentKey = trim($keys[$i]);
6186  if (is_object($value)) {
6187  $value = $value->{$currentKey};
6188  } elseif (is_array($value)) {
6189  $value = $value[$currentKey];
6190  } else {
6191  $value = '';
6192  break;
6193  }
6194  }
6195  if (!is_scalar($value)) {
6196  $value = '';
6197  }
6198  return $value;
6199  }
6200 
6211  public function getKey($key, $arr)
6212  {
6213  $key = (int)$key;
6214  if (is_array($arr)) {
6215  if ($key < 0) {
6216  $key = count($arr) + $key;
6217  }
6218  if ($key < 0) {
6219  $key = 0;
6220  }
6221  }
6222  return $key;
6223  }
6224 
6234  public function TCAlookup($inputValue, $conf)
6235  {
6236  $table = $conf['table'];
6237  $field = $conf['field'];
6238  $delimiter = $conf['delimiter'] ? $conf['delimiter'] : ' ,';
6239  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])) {
6240  $tsfe = $this->getTypoScriptFrontendController();
6241  $values = GeneralUtility::trimExplode(',', $inputValue);
6242  $output = [];
6243  foreach ($values as $value) {
6244  // Traverse the items-array...
6245  foreach ($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] as $item) {
6246  // ... and return the first found label where the value was equal to $key
6247  if ((string)$item[1] === trim($value)) {
6248  $output[] = $tsfe->sL($item[0]);
6249  }
6250  }
6251  }
6252  $returnValue = implode($delimiter, $output);
6253  } else {
6254  $returnValue = $inputValue;
6255  }
6256  return $returnValue;
6257  }
6258 
6259  /***********************************************
6260  *
6261  * Link functions (typolink)
6262  *
6263  ***********************************************/
6264 
6277  protected function resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration = [])
6278  {
6279  $linkParameter = null;
6280 
6281  // Link parameter value = first part
6282  $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
6283 
6284  // Check for link-handler keyword:
6285  list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2);
6286  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string)$linkHandlerValue !== '') {
6287  $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
6288  if (method_exists($linkHandlerObj, 'main')) {
6289  return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this);
6290  }
6291  }
6292 
6293  // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
6294  if ($linkHandlerKeyword === 'file' && !StringUtility::beginsWith($linkParameterParts['url'], 'file://')) {
6295  try {
6296  $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($linkHandlerValue);
6297  // Link to a folder or file
6298  if ($fileOrFolderObject instanceof File || $fileOrFolderObject instanceof Folder) {
6299  $linkParameter = $fileOrFolderObject->getPublicUrl();
6300  } else {
6301  $linkParameter = null;
6302  }
6303  } catch (\RuntimeException $e) {
6304  // Element wasn't found
6305  $linkParameter = null;
6306  } catch (ResourceDoesNotExistException $e) {
6307  // Resource was not found
6308  return $linkText;
6309  }
6310  // Disallow direct javascript: or data: links
6311  } elseif (in_array(strtolower(trim($linkHandlerKeyword)), ['javascript', 'data'], true)) {
6312  return $linkText;
6313  } else {
6314  $linkParameter = $linkParameterParts['url'];
6315  }
6316 
6317  // additional parameters that need to be set
6318  if ($linkParameterParts['additionalParams'] !== '') {
6319  $forceParams = $linkParameterParts['additionalParams'];
6320  // params value
6321  $configuration['additionalParams'] .= $forceParams[0] === '&' ? $forceParams : '&' . $forceParams;
6322  }
6323 
6324  return [
6325  'href' => $linkParameter,
6326  'target' => $linkParameterParts['target'],
6327  'class' => $linkParameterParts['class'],
6328  'title' => $linkParameterParts['title']
6329  ];
6330  }
6331 
6346  protected function detectLinkTypeFromLinkParameter($linkParameter)
6347  {
6348  if (stripos(rawurldecode(trim($linkParameter)), 'phar://') === 0) {
6349  throw new \RuntimeException(
6350  'phar scheme not allowed as soft reference target',
6351  1530030673
6352  );
6353  }
6354 
6355  // Parse URL:
6356  $scheme = parse_url($linkParameter, PHP_URL_SCHEME);
6357  // Detecting kind of link:
6358  // If it's a mail address:
6359  if (strpos($linkParameter, '@') > 0 && (!$scheme || $scheme === 'mailto')) {
6360  return 'mailto';
6361  }
6362 
6363  $isLocalFile = 0;
6364  $fileChar = intval(strpos($linkParameter, '/'));
6365  $urlChar = intval(strpos($linkParameter, '.'));
6366 
6367  $containsSlash = false;
6368  // Firsts, test if $linkParameter is numeric and page with such id exists. If yes, do not attempt to link to file
6369  if (!MathUtility::canBeInterpretedAsInteger($linkParameter) || empty($this->getTypoScriptFrontendController()->sys_page->getPage_noCheck($linkParameter))) {
6370  // Detects if a file is found in site-root and if so it will be treated like a normal file.
6371  list($rootFileDat) = explode('?', rawurldecode($linkParameter));
6372  $containsSlash = strpos($rootFileDat, '/') !== false;
6373  $rFD_fI = pathinfo($rootFileDat);
6374  $fileExtension = strtolower($rFD_fI['extension']);
6375  if (!$containsSlash && trim($rootFileDat) && (@is_file(PATH_site . $rootFileDat) || $fileExtension === 'php' || $fileExtension === 'html' || $fileExtension === 'htm')) {
6376  $isLocalFile = 1;
6377  } elseif ($containsSlash) {
6378  // Adding this so realurl directories are linked right (non-existing).
6379  $isLocalFile = 2;
6380  }
6381  }
6382 
6383  // url (external): If doubleSlash or if a '.' comes before a '/'.
6384  if ($scheme || $isLocalFile !== 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar)) {
6385  return 'url';
6386 
6387  // file (internal)
6388  } elseif ($containsSlash || $isLocalFile) {
6389  return 'file';
6390  }
6391 
6392  // Integer or alias (alias is without slashes or periods or commas, that is
6393  // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
6394  return 'page';
6395  }
6396 
6411  public function typoLink($linktxt, $conf)
6412  {
6413  $linktxt = (string)$linktxt;
6414  $tsfe = $this->getTypoScriptFrontendController();
6415 
6416  $LD = [];
6417  $finalTagParts = [];
6418  $finalTagParts['aTagParams'] = $this->getATagParams($conf);
6419  $linkParameter = trim(isset($conf['parameter.']) ? $this->stdWrap($conf['parameter'], $conf['parameter.']) : $conf['parameter']);
6420  $this->lastTypoLinkUrl = '';
6421  $this->lastTypoLinkTarget = '';
6422 
6423  $resolvedLinkParameters = $this->resolveMixedLinkParameter($linktxt, $linkParameter, $conf);
6424  // check if the link handler hook has resolved the link completely already
6425  if (!is_array($resolvedLinkParameters)) {
6426  return $resolvedLinkParameters;
6427  }
6428 
6429  $linkParameter = $resolvedLinkParameters['href'];
6430  $forceTarget = $resolvedLinkParameters['target'];
6431  $linkClass = $resolvedLinkParameters['class'];
6432  $forceTitle = $resolvedLinkParameters['title'];
6433 
6434  if (!$linkParameter) {
6435  return $linktxt;
6436  }
6437 
6438  // Check, if the target is coded as a JS open window link:
6439  $JSwindowParts = [];
6440  $JSwindowParams = '';
6441  if ($forceTarget && preg_match('/^([0-9]+)x([0-9]+)(:(.*)|.*)$/', $forceTarget, $JSwindowParts)) {
6442  // Take all pre-configured and inserted parameters and compile parameter list, including width+height:
6443  $JSwindow_tempParamsArr = GeneralUtility::trimExplode(',', strtolower($conf['JSwindow_params'] . ',' . $JSwindowParts[4]), true);
6444  $JSwindow_paramsArr = [];
6445  foreach ($JSwindow_tempParamsArr as $JSv) {
6446  list($JSp, $JSv) = explode('=', $JSv, 2);
6447  $JSwindow_paramsArr[$JSp] = $JSp . '=' . $JSv;
6448  }
6449  // Add width/height:
6450  $JSwindow_paramsArr['width'] = 'width=' . $JSwindowParts[1];
6451  $JSwindow_paramsArr['height'] = 'height=' . $JSwindowParts[2];
6452  // Imploding into string:
6453  $JSwindowParams = implode(',', $JSwindow_paramsArr);
6454  // Resetting the target since we will use onClick.
6455  $forceTarget = '';
6456  }
6457 
6458  // Internal target:
6459  if ($tsfe->dtdAllowsFrames) {
6460  $target = isset($conf['target']) ? $conf['target'] : $tsfe->intTarget;
6461  } else {
6462  $target = isset($conf['target']) ? $conf['target'] : '';
6463  }
6464  if ($conf['target.']) {
6465  $target = $this->stdWrap($target, $conf['target.']);
6466  }
6467 
6468  // Title tag
6469  $title = $conf['title'];
6470  if ($conf['title.']) {
6471  $title = $this->stdWrap($title, $conf['title.']);
6472  }
6473 
6474  $theTypeP = '';
6475  // Detecting kind of link
6476  $linkType = $this->detectLinkTypeFromLinkParameter($linkParameter);
6477  switch ($linkType) {
6478  // If it's a mail address
6479  case 'mailto':
6480  $linkParameter = preg_replace('/^mailto:/i', '', $linkParameter);
6481  list($this->lastTypoLinkUrl, $linktxt) = $this->getMailTo($linkParameter, $linktxt);
6482  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6483  break;
6484 
6485  // url (external): If doubleSlash or if a '.' comes before a '/'.
6486  case 'url':
6487  if ($tsfe->dtdAllowsFrames) {
6488  $target = isset($conf['extTarget']) ? $conf['extTarget'] : $tsfe->extTarget;
6489  } else {
6490  $target = isset($conf['extTarget']) ? $conf['extTarget'] : '';
6491  }
6492  if ($conf['extTarget.']) {
6493  $target = $this->stdWrap($target, $conf['extTarget.']);
6494  }
6495  if ($forceTarget) {
6496  $target = $forceTarget;
6497  }
6498  if ($linktxt === '') {
6499  $linktxt = $this->parseFunc($linkParameter, ['makelinks' => 0], '< lib.parseFunc');
6500  }
6501  // Parse URL:
6502  $urlParts = parse_url($linkParameter);
6503  if (!$urlParts['scheme']) {
6504  $scheme = 'http://';
6505  } else {
6506  $scheme = '';
6507  }
6508 
6509  $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_EXTERNAL, $scheme . $linkParameter, $conf);
6510 
6511  $this->lastTypoLinkTarget = $target;
6512  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6513  $finalTagParts['targetParams'] = $target ? ' target="' . htmlspecialchars($target) . '"' : '';
6514  $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], $linkType);
6515  break;
6516 
6517  // file (internal)
6518  case 'file':
6519 
6520  $splitLinkParam = explode('?', $linkParameter);
6521 
6522  // check if the file exists or if a / is contained (same check as in detectLinkType)
6523  if (file_exists(rawurldecode($splitLinkParam[0])) || strpos($linkParameter, '/') !== false) {
6524  $fileUri = $linkParameter;
6525  if ($linktxt === '') {
6526  $linktxt = $this->parseFunc(rawurldecode($linkParameter), ['makelinks' => 0], '< lib.parseFunc');
6527  }
6528  if (!StringUtility::beginsWith($linkParameter, '/')
6529  && parse_url($linkParameter, PHP_URL_SCHEME) === null
6530  ) {
6531  $fileUri = $tsfe->absRefPrefix . $fileUri;
6532  }
6533  $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_FILE, $fileUri, $conf);
6534  $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
6535  $target = isset($conf['fileTarget']) ? $conf['fileTarget'] : $tsfe->fileTarget;
6536  if ($conf['fileTarget.']) {
6537  $target = $this->stdWrap($target, $conf['fileTarget.']);
6538  }
6539  if ($forceTarget) {
6540  $target = $forceTarget;
6541  }
6542  $this->lastTypoLinkTarget = $target;
6543  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6544  $finalTagParts['targetParams'] = $target ? ' target="' . htmlspecialchars($target) . '"' : '';
6545  $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], $linkType);
6546  } else {
6547  $this->getTimeTracker()->setTSlogMessage('typolink(): File "' . $splitLinkParam[0] . '" did not exist, so "' . $linktxt . '" was not linked.', 1);
6548  return $linktxt;
6549  }
6550  break;
6551 
6552  // Integer or alias (alias is without slashes or periods or commas, that is
6553  // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
6554  case 'page':
6555  $enableLinksAcrossDomains = $tsfe->config['config']['typolinkEnableLinksAcrossDomains'];
6556 
6557  if ($conf['no_cache.']) {
6558  $conf['no_cache'] = $this->stdWrap($conf['no_cache'], $conf['no_cache.']);
6559  }
6560  // Splitting the parameter by ',' and if the array counts more than 1 element it's an id/type/parameters triplet
6561  $pairParts = GeneralUtility::trimExplode(',', $linkParameter, true);
6562  $linkParameter = $pairParts[0];
6563  $link_params_parts = explode('#', $linkParameter);
6564  // Link-data del
6565  $linkParameter = trim($link_params_parts[0]);
6566  // If no id or alias is given
6567  if ($linkParameter === '') {
6568  $linkParameter = $tsfe->id;
6569  }
6570 
6571  $sectionMark = trim(isset($conf['section.']) ? $this->stdWrap($conf['section'], $conf['section.']) : $conf['section']);
6572  if ($sectionMark !== '') {
6573  $sectionMark = '#' . (MathUtility::canBeInterpretedAsInteger($sectionMark) ? 'c' : '') . $sectionMark;
6574  }
6575 
6576  if ($link_params_parts[1] && $sectionMark === '') {
6577  $sectionMark = trim($link_params_parts[1]);
6578  $sectionMark = '#' . (MathUtility::canBeInterpretedAsInteger($sectionMark) ? 'c' : '') . $sectionMark;
6579  }
6580  if (count($pairParts) > 1) {
6581  // Overruling 'type'
6582  $theTypeP = isset($pairParts[1]) ? $pairParts[1] : 0;
6583  $conf['additionalParams'] .= isset($pairParts[2]) ? $pairParts[2] : '';
6584  }
6585  // Checking if the id-parameter is an alias.
6586  if (!MathUtility::canBeInterpretedAsInteger($linkParameter)) {
6587  $linkParameter = $tsfe->sys_page->getPageIdFromAlias($linkParameter);
6588  }
6589  // Link to page even if access is missing?
6590  if (isset($conf['linkAccessRestrictedPages'])) {
6591  $disableGroupAccessCheck = (bool)$conf['linkAccessRestrictedPages'];
6592  } else {
6593  $disableGroupAccessCheck = (bool)$tsfe->config['config']['typolinkLinkAccessRestrictedPages'];
6594  }
6595  // Looking up the page record to verify its existence:
6596  $page = $tsfe->sys_page->getPage($linkParameter, $disableGroupAccessCheck);
6597  if (!empty($page)) {
6598  // MointPoints, look for closest MPvar:
6599  $MPvarAcc = [];
6600  if (!$tsfe->config['config']['MP_disableTypolinkClosestMPvalue']) {
6601  $temp_MP = $this->getClosestMPvalueForPage($page['uid'], true);
6602  if ($temp_MP) {
6603  $MPvarAcc['closest'] = $temp_MP;
6604  }
6605  }
6606  // Look for overlay Mount Point:
6607  $mount_info = $tsfe->sys_page->getMountPointInfo($page['uid'], $page);
6608  if (is_array($mount_info) && $mount_info['overlay']) {
6609  $page = $tsfe->sys_page->getPage($mount_info['mount_pid'], $disableGroupAccessCheck);
6610  if (empty($page)) {
6611  $this->getTimeTracker()->setTSlogMessage('typolink(): Mount point "' . $mount_info['mount_pid'] . '" was not available, so "' . $linktxt . '" was not linked.', 1);
6612  return $linktxt;
6613  }
6614  $MPvarAcc['re-map'] = $mount_info['MPvar'];
6615  }
6616  // Setting title if blank value to link:
6617  if ($linktxt === '') {
6618  $linktxt = $this->parseFunc($page['title'], ['makelinks' => 0], '< lib.parseFunc');
6619  }
6620  // Query Params:
6621  $addQueryParams = $conf['addQueryString'] ? $this->getQueryArguments($conf['addQueryString.']) : '';
6622  $addQueryParams .= isset($conf['additionalParams.']) ? trim($this->stdWrap($conf['additionalParams'], $conf['additionalParams.'])) : trim($conf['additionalParams']);
6623  if ($addQueryParams === '&' || $addQueryParams[0] !== '&') {
6624  $addQueryParams = '';
6625  }
6626  $targetDomain = '';
6627  $currentDomain = (string)$this->getEnvironmentVariable('HTTP_HOST');
6628  // Mount pages are always local and never link to another domain
6629  if (!empty($MPvarAcc)) {
6630  // Add "&MP" var:
6631  $addQueryParams .= '&MP=' . rawurlencode(implode(',', $MPvarAcc));
6632  } elseif (strpos($addQueryParams, '&MP=') === false && $tsfe->config['config']['typolinkCheckRootline']) {
6633  // We do not come here if additionalParams had '&MP='. This happens when typoLink is called from
6634  // menu. Mount points always work in the content of the current domain and we must not change
6635  // domain if MP variables exist.
6636  // If we link across domains and page is free type shortcut, we must resolve the shortcut first!
6637  // If we do not do it, TYPO3 will fail to (1) link proper page in RealURL/CoolURI because
6638  // they return relative links and (2) show proper page if no RealURL/CoolURI exists when link is clicked
6639  if ($enableLinksAcrossDomains
6640  && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
6641  && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
6642  ) {
6643  // Save in case of broken destination or endless loop
6644  $page2 = $page;
6645  // Same as in RealURL, seems enough
6646  $maxLoopCount = 20;
6647  while ($maxLoopCount
6648  && is_array($page)
6649  && (int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT
6650  && (int)$page['shortcut_mode'] === PageRepository::SHORTCUT_MODE_NONE
6651  ) {
6652  $page = $tsfe->sys_page->getPage($page['shortcut'], $disableGroupAccessCheck);
6653  $maxLoopCount--;
6654  }
6655  if (empty($page) || $maxLoopCount === 0) {
6656  // We revert if shortcut is broken or maximum number of loops is exceeded (indicates endless loop)
6657  $page = $page2;
6658  }
6659  }
6660 
6661  $targetDomain = $tsfe->getDomainNameForPid($page['uid']);
6662  // Do not prepend the domain if it is the current hostname
6663  if (!$targetDomain || $tsfe->domainNameMatchesCurrentRequest($targetDomain)) {
6664  $targetDomain = '';
6665  }
6666  }
6667  if ($conf['useCacheHash']) {
6668  $params = $tsfe->linkVars . $addQueryParams . '&id=' . $page['uid'];
6669  if (trim($params, '& ') != '') {
6671  $cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
6672  $cHash = $cacheHash->generateForParameters($params);
6673  $addQueryParams .= $cHash ? '&cHash=' . $cHash : '';
6674  }
6675  unset($params);
6676  }
6677  $absoluteUrlScheme = 'http';
6678  // URL shall be absolute:
6679  if (isset($conf['forceAbsoluteUrl']) && $conf['forceAbsoluteUrl'] || $page['url_scheme'] > 0) {
6680  // Override scheme:
6681  if (isset($conf['forceAbsoluteUrl.']['scheme']) && $conf['forceAbsoluteUrl.']['scheme']) {
6682  $absoluteUrlScheme = $conf['forceAbsoluteUrl.']['scheme'];
6683  } elseif ($page['url_scheme'] > 0) {
6684  $absoluteUrlScheme = (int)$page['url_scheme'] === HttpUtility::SCHEME_HTTP ? 'http' : 'https';
6685  } elseif ($this->getEnvironmentVariable('TYPO3_SSL')) {
6686  $absoluteUrlScheme = 'https';
6687  }
6688  // If no domain records are defined, use current domain:
6689  $currentUrlScheme = parse_url($this->getEnvironmentVariable('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
6690  if ($targetDomain === '' && ($conf['forceAbsoluteUrl'] || $absoluteUrlScheme !== $currentUrlScheme)) {
6691  $targetDomain = $currentDomain;
6692  }
6693  // If go for an absolute link, add site path if it's not taken care about by absRefPrefix
6694  if (!$tsfe->config['config']['absRefPrefix'] && $targetDomain === $currentDomain) {
6695  $targetDomain = $currentDomain . rtrim($this->getEnvironmentVariable('TYPO3_SITE_PATH'), '/');
6696  }
6697  }
6698  // If target page has a different domain and the current domain's linking scheme (e.g. RealURL/...) should not be used
6699  if ($targetDomain !== '' && $targetDomain !== $currentDomain && !$enableLinksAcrossDomains) {
6700  $target = isset($conf['extTarget']) ? $conf['extTarget'] : $tsfe->extTarget;
6701  if ($conf['extTarget.']) {
6702  $target = $this->stdWrap($target, $conf['extTarget.']);
6703  }
6704  if ($forceTarget) {
6705  $target = $forceTarget;
6706  }
6707  $LD['target'] = $target;
6708  // Convert IDNA-like domain (if any)
6709  if (!preg_match('/^[a-z0-9.\\-]*$/i', $targetDomain)) {
6710  $targetDomain = GeneralUtility::idnaEncode($targetDomain);
6711  }
6712  $this->lastTypoLinkUrl = $this->URLqMark($absoluteUrlScheme . '://' . $targetDomain . '/index.php?id=' . $page['uid'], $addQueryParams) . $sectionMark;
6713  } else {
6714  // Internal link or current domain's linking scheme should be used
6715  if ($forceTarget) {
6716  $target = $forceTarget;
6717  }
6718  $LD = $tsfe->tmpl->linkData($page, $target, $conf['no_cache'], '', '', $addQueryParams, $theTypeP, $targetDomain);
6719  if ($targetDomain !== '') {
6720  // We will add domain only if URL does not have it already.
6721  if ($enableLinksAcrossDomains && $targetDomain !== $currentDomain) {
6722  // Get rid of the absRefPrefix if necessary. absRefPrefix is applicable only
6723  // to the current web site. If we have domain here it means we link across
6724  // domains. absRefPrefix can contain domain name, which will screw up
6725  // the link to the external domain.
6726  $prefixLength = strlen($tsfe->config['config']['absRefPrefix']);
6727  if (substr($LD['totalURL'], 0, $prefixLength) === $tsfe->config['config']['absRefPrefix']) {
6728  $LD['totalURL'] = substr($LD['totalURL'], $prefixLength);
6729  }
6730  }
6731  $urlParts = parse_url($LD['totalURL']);
6732  if (empty($urlParts['host'])) {
6733  $LD['totalURL'] = $absoluteUrlScheme . '://' . $targetDomain . ($LD['totalURL'][0] === '/' ? '' : '/') . $LD['totalURL'];
6734  }
6735  }
6736  $this->lastTypoLinkUrl = $this->URLqMark($LD['totalURL'], '') . $sectionMark;
6737  }
6738  $this->lastTypoLinkTarget = $LD['target'];
6739  // If sectionMark is set, there is no baseURL AND the current page is the page the link is to, check if there are any additional parameters or addQueryString parameters and if not, drop the url.
6740  if ($sectionMark
6741  && !$tsfe->config['config']['baseURL']
6742  && (int)$page['uid'] === (int)$tsfe->id
6743  && !trim($addQueryParams)
6744  && (empty($conf['addQueryString']) || !isset($conf['addQueryString.']))
6745  ) {
6746  $currentQueryParams = $this->getQueryArguments([]);
6747  if (!trim($currentQueryParams)) {
6748  list(, $URLparams) = explode('?', $this->lastTypoLinkUrl);
6749  list($URLparams) = explode('#', $URLparams);
6750  parse_str($URLparams . $LD['orig_type'], $URLparamsArray);
6751  // Type nums must match as well as page ids
6752  if ((int)$URLparamsArray['type'] === (int)$tsfe->type) {
6753  unset($URLparamsArray['id']);
6754  unset($URLparamsArray['type']);
6755  // If there are no parameters left.... set the new url.
6756  if (empty($URLparamsArray)) {
6757  $this->lastTypoLinkUrl = $sectionMark;
6758  }
6759  }
6760  }
6761  }
6762  // If link is to an access restricted page which should be redirected, then find new URL:
6763  if (empty($conf['linkAccessRestrictedPages'])
6764  && $tsfe->config['config']['typolinkLinkAccessRestrictedPages']
6765  && $tsfe->config['config']['typolinkLinkAccessRestrictedPages'] !== 'NONE'
6766  && !$tsfe->checkPageGroupAccess($page)
6767  ) {
6768  $thePage = $tsfe->sys_page->getPage($tsfe->config['config']['typolinkLinkAccessRestrictedPages']);
6769  $addParams = str_replace(
6770  [
6771  '###RETURN_URL###',
6772  '###PAGE_ID###'
6773  ],
6774  [
6775  rawurlencode($this->lastTypoLinkUrl),
6776  $page['uid']
6777  ],
6778  $tsfe->config['config']['typolinkLinkAccessRestrictedPages_addParams']
6779  );
6780  $this->lastTypoLinkUrl = $this->getTypoLink_URL($thePage['uid'] . ($theTypeP ? ',' . $theTypeP : ''), $addParams, $target);
6781  $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
6782  $this->lastTypoLinkLD['totalUrl'] = $this->lastTypoLinkUrl;
6783  $LD = $this->lastTypoLinkLD;
6784  }
6785  // Rendering the tag.
6786  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6787  $finalTagParts['targetParams'] = (string)$LD['target'] !== '' ? ' target="' . htmlspecialchars($LD['target']) . '"' : '';
6788  } else {
6789  $this->getTimeTracker()->setTSlogMessage('typolink(): Page id "' . $linkParameter . '" was not found, so "' . $linktxt . '" was not linked.', 1);
6790  return $linktxt;
6791  }
6792  break;
6793  }
6794 
6795  $finalTagParts['TYPE'] = $linkType;
6796  $this->lastTypoLinkLD = $LD;
6797 
6798  if ($forceTitle) {
6799  $title = $forceTitle;
6800  }
6801 
6802  if ($JSwindowParams) {
6803  // Create TARGET-attribute only if the right doctype is used
6804  $xhtmlDocType = $tsfe->xhtmlDoctype;
6805  if ($xhtmlDocType !== 'xhtml_strict' && $xhtmlDocType !== 'xhtml_11' && $xhtmlDocType !== 'xhtml_2') {
6806  $target = ' target="FEopenLink"';
6807  } else {
6808  $target = '';
6809  }
6810  $onClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($finalTagParts['url'])) . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSwindowParams) . ');vHWin.focus();return false;';
6811  $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"'
6812  . $target
6813  . ' onclick="' . htmlspecialchars($onClick) . '"'
6814  . ((string)$title !== '' ? ' title="' . htmlspecialchars($title) . '"' : '')
6815  . ($linkClass !== '' ? ' class="' . htmlspecialchars($linkClass) . '"' : '')
6816  . $finalTagParts['aTagParams']
6817  . '>';
6818  } else {
6819  if ($tsfe->spamProtectEmailAddresses === 'ascii' && $linkType === 'mailto') {
6820  $finalAnchorTag = '<a href="' . $finalTagParts['url'] . '"';
6821  } else {
6822  $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"';
6823  }
6824  $finalAnchorTag .= ((string)$title !== '' ? ' title="' . htmlspecialchars($title) . '"' : '')
6825  . $finalTagParts['targetParams']
6826  . ($linkClass ? ' class="' . htmlspecialchars($linkClass) . '"' : '')
6827  . $finalTagParts['aTagParams']
6828  . '>';
6829  }
6830 
6831  // Call user function:
6832  if ($conf['userFunc']) {
6833  $finalTagParts['TAG'] = $finalAnchorTag;
6834  $finalAnchorTag = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $finalTagParts);
6835  }
6836 
6837  // Hook: Call post processing function for link rendering:
6838  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'])) {
6839  $_params = [
6840  'conf' => &$conf,
6841  'linktxt' => &$linktxt,
6842  'finalTag' => &$finalAnchorTag,
6843  'finalTagParts' => &$finalTagParts
6844  ];
6845  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'] as $_funcRef) {
6846  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
6847  }
6848  }
6849 
6850  // If flag "returnLastTypoLinkUrl" set, then just return the latest URL made:
6851  if ($conf['returnLast']) {
6852  switch ($conf['returnLast']) {
6853  case 'url':
6854  return $this->lastTypoLinkUrl;
6855  break;
6856  case 'target':
6858  break;
6859  }
6860  }
6861 
6862  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
6863 
6864  if ($conf['ATagBeforeWrap']) {
6865  return $finalAnchorTag . $this->wrap($linktxt, $wrap) . '</a>';
6866  }
6867  return $this->wrap($finalAnchorTag . $linktxt . '</a>', $wrap);
6868  }
6869 
6877  protected function forceAbsoluteUrl($url, array $configuration)
6878  {
6879  if (!empty($url) && !empty($configuration['forceAbsoluteUrl']) && preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', $url, $matches)) {
6880  $urlParts = [
6881  'scheme' => $matches[1],
6882  'delimiter' => '://',
6883  'host' => $matches[3],
6884  'path' => $matches[4]
6885  ];
6886  $isUrlModified = false;
6887  // Set scheme and host if not yet part of the URL:
6888  if (empty($urlParts['host'])) {
6889  $urlParts['scheme'] = $this->getEnvironmentVariable('TYPO3_SSL') ? 'https' : 'http';
6890  $urlParts['host'] = $this->getEnvironmentVariable('HTTP_HOST');
6891  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
6892  // absRefPrefix has been prepended to $url beforehand
6893  // so we only modify the path if no absRefPrefix has been set
6894  // otherwise we would destroy the path
6895  if ($this->getTypoScriptFrontendController()->absRefPrefix === '') {
6896  $urlParts['path'] = $this->getEnvironmentVariable('TYPO3_SITE_PATH') . ltrim($urlParts['path'], '/');
6897  }
6898  $isUrlModified = true;
6899  }
6900  // Override scheme:
6901  $forceAbsoluteUrl = &$configuration['forceAbsoluteUrl.']['scheme'];
6902  if (!empty($forceAbsoluteUrl) && $urlParts['scheme'] !== $forceAbsoluteUrl) {
6903  $urlParts['scheme'] = $forceAbsoluteUrl;
6904  $isUrlModified = true;
6905  }
6906  // Recreate the absolute URL:
6907  if ($isUrlModified) {
6908  $url = implode('', $urlParts);
6909  }
6910  }
6911  return $url;
6912  }
6913 
6921  public function typoLink_URL($conf)
6922  {
6923  $this->typoLink('|', $conf);
6924  return $this->lastTypoLinkUrl;
6925  }
6926 
6940  public function getTypoLink($label, $params, $urlParameters = [], $target = '')
6941  {
6942  $conf = [];
6943  $conf['parameter'] = $params;
6944  if ($target) {
6945  $conf['target'] = $target;
6946  $conf['extTarget'] = $target;
6947  $conf['fileTarget'] = $target;
6948  }
6949  if (is_array($urlParameters)) {
6950  if (!empty($urlParameters)) {
6951  $conf['additionalParams'] .= GeneralUtility::implodeArrayForUrl('', $urlParameters);
6952  }
6953  } else {
6954  $conf['additionalParams'] .= $urlParameters;
6955  }
6956  $out = $this->typoLink($label, $conf);
6957  return $out;
6958  }
6959 
6967  public function getUrlToCurrentLocation($addQueryString = true)
6968  {
6969  $conf = [];
6970  $conf['parameter'] = $this->getTypoScriptFrontendController()->id . ',' . $this->getTypoScriptFrontendController()->type;
6971  if ($addQueryString) {
6972  $conf['addQueryString'] = '1';
6973  $linkVars = implode(',', array_keys(GeneralUtility::explodeUrl2Array($this->getTypoScriptFrontendController()->linkVars)));
6974  $conf['addQueryString.'] = [
6975  'method' => 'GET',
6976  'exclude' => 'id,type,cHash' . ($linkVars ? ',' . $linkVars : '')
6977  ];
6978  $conf['useCacheHash'] = GeneralUtility::_GET('cHash') ? '1' : '0';
6979  }
6980 
6981  return $this->typoLink_URL($conf);
6982  }
6983 
6993  public function getTypoLink_URL($params, $urlParameters = [], $target = '')
6994  {
6995  $this->getTypoLink('', $params, $urlParameters, $target);
6996  return $this->lastTypoLinkUrl;
6997  }
6998 
7006  public function typolinkWrap($conf)
7007  {
7008  $k = md5(microtime());
7009  return explode($k, $this->typoLink($k, $conf));
7010  }
7011 
7020  public function currentPageUrl($urlParameters = [], $id = 0)
7021  {
7022  $tsfe = $this->getTypoScriptFrontendController();
7023  return $this->getTypoLink_URL($id ?: $tsfe->id, $urlParameters, $tsfe->sPre);
7024  }
7025 
7035  protected function processUrl($context, $url, $typolinkConfiguration = [])
7036  {
7037  if (
7038  empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
7039  || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
7040  ) {
7041  return $url;
7042  }
7043 
7044  $urlProcessors = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'];
7045  foreach ($urlProcessors as $identifier => $configuration) {
7046  if (empty($configuration) || !is_array($configuration)) {
7047  throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1442050529);
7048  }
7049  if (!is_string($configuration['processor']) || empty($configuration['processor']) || !class_exists($configuration['processor']) || !is_subclass_of($configuration['processor'], UrlProcessorInterface::class)) {
7050  throw new \RuntimeException('The URI processor "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlProcessorInterface::class . '".', 1442050579);
7051  }
7052  }
7053 
7054  $orderedProcessors = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlProcessors);
7055  $keepProcessing = true;
7056 
7057  foreach ($orderedProcessors as $configuration) {
7059  $urlProcessor = GeneralUtility::makeInstance($configuration['processor']);
7060  $url = $urlProcessor->process($context, $url, $typolinkConfiguration, $this, $keepProcessing);
7061  if (!$keepProcessing) {
7062  break;
7063  }
7064  }
7065 
7066  return $url;
7067  }
7068 
7078  public function getClosestMPvalueForPage($pageId, $raw = false)
7079  {
7080  $tsfe = $this->getTypoScriptFrontendController();
7081  if (empty($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) || !$tsfe->MP) {
7082  return '';
7083  }
7084  // MountPoints:
7085  $MP = '';
7086  // Same page as current.
7087  if ((int)$tsfe->id === (int)$pageId) {
7088  $MP = $tsfe->MP;
7089  } else {
7090  // ... otherwise find closest meeting point:
7091  // Gets rootline of linked-to page
7092  $tCR_rootline = $tsfe->sys_page->getRootLine($pageId, '', true);
7093  $inverseTmplRootline = array_reverse($tsfe->tmpl->rootLine);
7094  $rl_mpArray = [];
7095  $startMPaccu = false;
7096  // Traverse root line of link uid and inside of that the REAL root line of current position.
7097  foreach ($tCR_rootline as $tCR_data) {
7098  foreach ($inverseTmplRootline as $rlKey => $invTmplRLRec) {
7099  // Force accumulating when in overlay mode: Links to this page have to stay within the current branch
7100  if ($invTmplRLRec['_MOUNT_OL'] && (int)$tCR_data['uid'] === (int)$invTmplRLRec['uid']) {
7101  $startMPaccu = true;
7102  }
7103  // Accumulate MP data:
7104  if ($startMPaccu && $invTmplRLRec['_MP_PARAM']) {
7105  $rl_mpArray[] = $invTmplRLRec['_MP_PARAM'];
7106  }
7107  // If two PIDs matches and this is NOT the site root, start accumulation of MP data (on the next level):
7108  // (The check for site root is done so links to branches outsite the site but sharing the site roots PID
7109  // is NOT detected as within the branch!)
7110  if ((int)$tCR_data['pid'] === (int)$invTmplRLRec['pid'] && count($inverseTmplRootline) !== $rlKey + 1) {
7111  $startMPaccu = true;
7112  }
7113  }
7114  if ($startMPaccu) {
7115  // Good enough...
7116  break;
7117  }
7118  }
7119  if (!empty($rl_mpArray)) {
7120  $MP = implode(',', array_reverse($rl_mpArray));
7121  }
7122  }
7123  return $raw ? $MP : ($MP ? '&MP=' . rawurlencode($MP) : '');
7124  }
7125 
7135  public function getMailTo($mailAddress, $linktxt)
7136  {
7137  $mailAddress = (string)$mailAddress;
7138  if ((string)$linktxt === '') {
7139  $linktxt = htmlspecialchars($mailAddress);
7140  }
7141 
7142  $originalMailToUrl = 'mailto:' . $mailAddress;
7143  $mailToUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_MAIL, $originalMailToUrl);
7144 
7145  $tsfe = $this->getTypoScriptFrontendController();
7146  // no processing happened, therefore
7147  if ($mailToUrl === $originalMailToUrl) {
7148  if ($tsfe->spamProtectEmailAddresses) {
7149  if ($tsfe->spamProtectEmailAddresses === 'ascii') {
7150  $mailToUrl = $tsfe->encryptEmail($mailToUrl);
7151  } else {
7152  $mailToUrl = 'javascript:linkTo_UnCryptMailto(' . GeneralUtility::quoteJSvalue($tsfe->encryptEmail($mailToUrl)) . ');';
7153  }
7154  $atLabel = '';
7155  if ($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) {
7156  $atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']);
7157  }
7158  $spamProtectedMailAddress = str_replace('@', $atLabel ? $atLabel : '(at)', htmlspecialchars($mailAddress));
7159  if ($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']) {
7160  $lastDotLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']);
7161  $lastDotLabel = $lastDotLabel ? $lastDotLabel : '(dot)';
7162  $spamProtectedMailAddress = preg_replace('/\\.([^\\.]+)$/', $lastDotLabel . '$1', $spamProtectedMailAddress);
7163  }
7164  $linktxt = str_ireplace($mailAddress, $spamProtectedMailAddress, $linktxt);
7165  }
7166  }
7167 
7168  return [$mailToUrl, $linktxt];
7169  }
7170 
7180  public function getQueryArguments($conf, $overruleQueryArguments = [], $forceOverruleArguments = false)
7181  {
7182  switch ((string)$conf['method']) {
7183  case 'GET':
7184  $currentQueryArray = GeneralUtility::_GET();
7185  break;
7186  case 'POST':
7187  $currentQueryArray = GeneralUtility::_POST();
7188  break;
7189  case 'GET,POST':
7190  $currentQueryArray = GeneralUtility::_GET();
7192  break;
7193  case 'POST,GET':
7194  $currentQueryArray = GeneralUtility::_POST();
7196  break;
7197  default:
7198  $currentQueryArray = GeneralUtility::explodeUrl2Array($this->getEnvironmentVariable('QUERY_STRING'), true);
7199  }
7200  if ($conf['exclude']) {
7201  $exclude = str_replace(',', '&', $conf['exclude']);
7202  $exclude = GeneralUtility::explodeUrl2Array($exclude, true);
7203  // never repeat id
7204  $exclude['id'] = 0;
7205  $newQueryArray = ArrayUtility::arrayDiffAssocRecursive($currentQueryArray, $exclude);
7206  } else {
7207  $newQueryArray = $currentQueryArray;
7208  }
7209  if ($forceOverruleArguments) {
7210  ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments);
7211  } else {
7212  ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments, false);
7213  }
7214  return GeneralUtility::implodeArrayForUrl('', $newQueryArray, '', false, true);
7215  }
7216 
7217  /***********************************************
7218  *
7219  * Miscellaneous functions, stand alone
7220  *
7221  ***********************************************/
7233  public function wrap($content, $wrap, $char = '|')
7234  {
7235  if ($wrap) {
7236  $wrapArr = explode($char, $wrap);
7237  $content = trim($wrapArr[0]) . $content . trim($wrapArr[1]);
7238  }
7239  return $content;
7240  }
7241 
7252  public function noTrimWrap($content, $wrap, $char = '|')
7253  {
7254  if ($wrap) {
7255  // expects to be wrapped with (at least) 3 characters (before, middle, after)
7256  // anything else is not taken into account
7257  $wrapArr = explode($char, $wrap, 4);
7258  $content = $wrapArr[1] . $content . $wrapArr[2];
7259  }
7260  return $content;
7261  }
7262 
7271  public function wrapSpace($content, $wrap, array $conf = null)
7272  {
7273  if (trim($wrap)) {
7274  $wrapArray = explode('|', $wrap);
7275  $wrapBefore = (int)$wrapArray[0];
7276  $wrapAfter = (int)$wrapArray[1];
7277  $useDivTag = isset($conf['useDiv']) && $conf['useDiv'];
7278  if ($wrapBefore) {
7279  if ($useDivTag) {
7280  $content = '<div class="content-spacer spacer-before" style="height:' . $wrapBefore . 'px;"></div>' . $content;
7281  } else {
7282  $content = '<span style="width: 1px; height: ' . $wrapBefore . 'px; display: inline-block;"></span><br />' . $content;
7283  }
7284  }
7285  if ($wrapAfter) {
7286  if ($useDivTag) {
7287  $content .= '<div class="content-spacer spacer-after" style="height:' . $wrapAfter . 'px;"></div>';
7288  } else {
7289  $content .= '<span style="width: 1px; height: ' . $wrapAfter . 'px; display: inline-block;"></span><br />';
7290  }
7291  }
7292  }
7293  return $content;
7294  }
7295 
7306  public function callUserFunction($funcName, $conf, $content)
7307  {
7308  // Split parts
7309  $parts = explode('->', $funcName);
7310  if (count($parts) === 2) {
7311  // Class
7312  // Check whether class is available and try to reload includeLibs if possible:
7313  if ($this->isClassAvailable($parts[0], $conf)) {
7314  $classObj = GeneralUtility::makeInstance($parts[0]);
7315  if (is_object($classObj) && method_exists($classObj, $parts[1])) {
7316  $classObj->cObj = $this;
7317  $content = call_user_func_array([
7318  $classObj,
7319  $parts[1]
7320  ], [
7321  $content,
7322  $conf
7323  ]);
7324  } else {
7325  $this->getTimeTracker()->setTSlogMessage('Method "' . $parts[1] . '" did not exist in class "' . $parts[0] . '"', 3);
7326  }
7327  } else {
7328  $this->getTimeTracker()->setTSlogMessage('Class "' . $parts[0] . '" did not exist', 3);
7329  }
7330  } elseif (function_exists($funcName)) {
7331  $content = call_user_func($funcName, $content, $conf);
7332  } else {
7333  $this->getTimeTracker()->setTSlogMessage('Function "' . $funcName . '" did not exist', 3);
7334  }
7335  return $content;
7336  }
7337 
7345  public function processParams($params)
7346  {
7347  $paramArr = [];
7348  $lines = GeneralUtility::trimExplode(LF, $params, true);
7349  foreach ($lines as $val) {
7350  $pair = explode('=', $val, 2);
7351  $key = trim($pair[0]);
7352  if ($key[0] !== '#' && $key[0] !== '/') {
7353  $paramArr[$key] = trim($pair[1]);
7354  }
7355  }
7356  return $paramArr;
7357  }
7358 
7365  public function keywords($content)
7366  {
7367  $listArr = preg_split('/[,;' . LF . ']/', $content);
7368  foreach ($listArr as $k => $v) {
7369  $listArr[$k] = trim($v);
7370  }
7371  return implode(',', $listArr);
7372  }
7373 
7382  public function caseshift($theValue, $case)
7383  {
7384  $tsfe = $this->getTypoScriptFrontendController();
7385  switch (strtolower($case)) {
7386  case 'upper':
7387  $theValue = $tsfe->csConvObj->conv_case($tsfe->renderCharset, $theValue, 'toUpper');
7388  break;
7389  case 'lower':
7390  $theValue = $tsfe->csConvObj->conv_case($tsfe->renderCharset, $theValue, 'toLower');
7391  break;
7392  case 'capitalize':
7393  $theValue = $tsfe->csConvObj->convCapitalize($tsfe->renderCharset, $theValue);
7394  break;
7395  case 'ucfirst':
7396  $theValue = $tsfe->csConvObj->convCaseFirst($tsfe->renderCharset, $theValue, 'toUpper');
7397  break;
7398  case 'lcfirst':
7399  $theValue = $tsfe->csConvObj->convCaseFirst($tsfe->renderCharset, $theValue, 'toLower');
7400  break;
7401  case 'uppercamelcase':
7402  $theValue = GeneralUtility::underscoredToUpperCamelCase($theValue);
7403  break;
7404  case 'lowercamelcase':
7405  $theValue = GeneralUtility::underscoredToLowerCamelCase($theValue);
7406  break;
7407  }
7408  return $theValue;
7409  }
7410 
7419  public function HTMLcaseshift($theValue, $case)
7420  {
7421  $inside = 0;
7422  $newVal = '';
7423  $pointer = 0;
7424  $totalLen = strlen($theValue);
7425  do {
7426  if (!$inside) {
7427  $len = strcspn(substr($theValue, $pointer), '<');
7428  $newVal .= $this->caseshift(substr($theValue, $pointer, $len), $case);
7429  $inside = 1;
7430  } else {
7431  $len = strcspn(substr($theValue, $pointer), '>') + 1;
7432  $newVal .= substr($theValue, $pointer, $len);
7433  $inside = 0;
7434  }
7435  $pointer += $len;
7436  } while ($pointer < $totalLen);
7437  return $newVal;
7438  }
7439 
7447  public function calcAge($seconds, $labels)
7448  {
7450  $labels = ' min| hrs| days| yrs| min| hour| day| year';
7451  } else {
7452  $labels = str_replace('"', '', $labels);
7453  }
7454  $labelArr = explode('|', $labels);
7455  if (count($labelArr) === 4) {
7456  $labelArr = array_merge($labelArr, $labelArr);
7457  }
7458  $absSeconds = abs($seconds);
7459  $sign = $seconds > 0 ? 1 : -1;
7460  if ($absSeconds < 3600) {
7461  $val = round($absSeconds / 60);
7462  $seconds = $sign * $val . ($val == 1 ? $labelArr[4] : $labelArr[0]);
7463  } elseif ($absSeconds < 24 * 3600) {
7464  $val = round($absSeconds / 3600);
7465  $seconds = $sign * $val . ($val == 1 ? $labelArr[5] : $labelArr[1]);
7466  } elseif ($absSeconds < 365 * 24 * 3600) {
7467  $val = round($absSeconds / (24 * 3600));
7468  $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
7469  } else {
7470  $val = round($absSeconds / (365 * 24 * 3600));
7471  $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
7472  }
7473  return $seconds;
7474  }
7475 
7487  public function sendNotifyEmail($message, $recipients, $cc, $senderAddress, $senderName = '', $replyTo = '')
7488  {
7490  $mail = GeneralUtility::makeInstance(MailMessage::class);
7491  $senderName = trim($senderName);
7492  $senderAddress = trim($senderAddress);
7493  if ($senderName !== '' && $senderAddress !== '') {
7494  $sender = [$senderAddress => $senderName];
7495  } elseif ($senderAddress !== '') {
7496  $sender = [$senderAddress];
7497  } else {
7498  $sender = MailUtility::getSystemFrom();
7499  }
7500  $mail->setFrom($sender);
7501  $parsedReplyTo = MailUtility::parseAddresses($replyTo);
7502  if (!empty($parsedReplyTo)) {
7503  $mail->setReplyTo($parsedReplyTo);
7504  }
7505  $message = trim($message);
7506  if ($message !== '') {
7507  // First line is subject
7508  $messageParts = explode(LF, $message, 2);
7509  $subject = trim($messageParts[0]);
7510  $plainMessage = trim($messageParts[1]);
7511  $parsedRecipients = MailUtility::parseAddresses($recipients);
7512  if (!empty($parsedRecipients)) {
7513  $mail->setTo($parsedRecipients)
7514  ->setSubject($subject)
7515  ->setBody($plainMessage);
7516  $mail->send();
7517  }
7518  $parsedCc = MailUtility::parseAddresses($cc);
7519  if (!empty($parsedCc)) {
7521  $mail = GeneralUtility::makeInstance(MailMessage::class);
7522  if (!empty($parsedReplyTo)) {
7523  $mail->setReplyTo($parsedReplyTo);
7524  }
7525  $mail->setFrom($sender)
7526  ->setTo($parsedCc)
7527  ->setSubject($subject)
7528  ->setBody($plainMessage);
7529  $mail->send();
7530  }
7531  return true;
7532  }
7533  return false;
7534  }
7535 
7543  public function URLqMark($url, $params)
7544  {
7545  if ($params && !strstr($url, '?')) {
7546  return $url . '?' . $params;
7547  } else {
7548  return $url . $params;
7549  }
7550  }
7551 
7560  public function clearTSProperties($TSArr, $propList)
7561  {
7562  $list = explode(',', $propList);
7563  foreach ($list as $prop) {
7564  $prop = trim($prop);
7565  unset($TSArr[$prop]);
7566  unset($TSArr[$prop . '.']);
7567  }
7568  return $TSArr;
7569  }
7570 
7579  public function mergeTSRef($confArr, $prop)
7580  {
7581  if ($confArr[$prop][0] === '<') {
7582  $key = trim(substr($confArr[$prop], 1));
7583  $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
7584  // $name and $conf is loaded with the referenced values.
7585  $old_conf = $confArr[$prop . '.'];
7586  list(, $conf) = $cF->getVal($key, $this->getTypoScriptFrontendController()->tmpl->setup);
7587  if (is_array($old_conf) && !empty($old_conf)) {
7588  $conf = is_array($conf) ? array_replace_recursive($conf, $old_conf) : $old_conf;
7589  }
7590  $confArr[$prop . '.'] = $conf;
7591  }
7592  return $confArr;
7593  }
7594 
7603  public function gifBuilderTextBox($gifbuilderConf, $conf, $text)
7604  {
7605  $chars = (int)$conf['chars'] ?: 20;
7606  $lineDist = (int)$conf['lineDist'] ?: 20;
7607  $Valign = strtolower(trim($conf['Valign']));
7608  $tmplObjNumber = (int)$conf['tmplObjNumber'];
7609  $maxLines = (int)$conf['maxLines'];
7610  if ($tmplObjNumber && $gifbuilderConf[$tmplObjNumber] === 'TEXT') {
7611  $textArr = $this->linebreaks($text, $chars, $maxLines);
7612  $angle = (int)$gifbuilderConf[$tmplObjNumber . '.']['angle'];
7613  foreach ($textArr as $c => $textChunk) {
7614  $index = $tmplObjNumber + 1 + $c * 2;
7615  // Workarea
7616  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
7617  $rad_angle = 2 * pi() / 360 * $angle;
7618  $x_d = sin($rad_angle) * $lineDist;
7619  $y_d = cos($rad_angle) * $lineDist;
7620  $diff_x_d = 0;
7621  $diff_y_d = 0;
7622  if ($Valign === 'center') {
7623  $diff_x_d = $x_d * count($textArr);
7624  $diff_x_d = $diff_x_d / 2;
7625  $diff_y_d = $y_d * count($textArr);
7626  $diff_y_d = $diff_y_d / 2;
7627  }
7628  $x_d = round($x_d * $c - $diff_x_d);
7629  $y_d = round($y_d * $c - $diff_y_d);
7630  $gifbuilderConf[$index] = 'WORKAREA';
7631  $gifbuilderConf[$index . '.']['set'] = $x_d . ',' . $y_d;
7632  // Text
7633  $index++;
7634  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
7635  $gifbuilderConf[$index] = 'TEXT';
7636  $gifbuilderConf[$index . '.'] = $this->clearTSProperties($gifbuilderConf[$tmplObjNumber . '.'], 'text');
7637  $gifbuilderConf[$index . '.']['text'] = $textChunk;
7638  }
7639  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $tmplObjNumber);
7640  }
7641  return $gifbuilderConf;
7642  }
7643 
7654  public function linebreaks($string, $chars, $maxLines = 0)
7655  {
7656  $lines = explode(LF, $string);
7657  $lineArr = [];
7658  $c = 0;
7659  foreach ($lines as $paragraph) {
7660  $words = explode(' ', $paragraph);
7661  foreach ($words as $word) {
7662  if (strlen($lineArr[$c] . $word) > $chars) {
7663  $c++;
7664  }
7665  if (!$maxLines || $c < $maxLines) {
7666  $lineArr[$c] .= $word . ' ';
7667  }
7668  }
7669  $c++;
7670  }
7671  return $lineArr;
7672  }
7673 
7680  public function includeLibs(array $config)
7681  {
7682  $librariesIncluded = false;
7683  if (isset($config['includeLibs']) && $config['includeLibs']) {
7684  $libraries = GeneralUtility::trimExplode(',', $config['includeLibs'], true);
7685  $this->getTypoScriptFrontendController()->includeLibraries($libraries);
7686  $librariesIncluded = true;
7687  }
7688  return $librariesIncluded;
7689  }
7690 
7710  protected function isClassAvailable($className, array $config = null)
7711  {
7712  if (class_exists($className)) {
7713  return true;
7714  }
7715  if ($config) {
7716  $pluginConfiguration = &$this->getTypoScriptFrontendController()->tmpl->setup['plugin.'][$className . '.'];
7717  if (isset($pluginConfiguration['includeLibs']) && $pluginConfiguration['includeLibs']) {
7718  $config['includeLibs'] = $pluginConfiguration['includeLibs'];
7719  return $this->includeLibs($config);
7720  }
7721  }
7722  return false;
7723  }
7724 
7725  /***********************************************
7726  *
7727  * Database functions, making of queries
7728  *
7729  ***********************************************/
7740  public function DBgetDelete($table, $uid, $doExec = false)
7741  {
7742  $uid = (int)$uid;
7743  if (!$uid) {
7744  return '';
7745  }
7746  $db = $this->getDatabaseConnection();
7747  if ($GLOBALS['TCA'][$table]['ctrl']['delete']) {
7748  $updateFields = [];
7749  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['delete']] = 1;
7750  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7751  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
7752  }
7753  if ($doExec) {
7754  return $db->exec_UPDATEquery($table, 'uid=' . $uid, $updateFields);
7755  } else {
7756  return $db->UPDATEquery($table, 'uid=' . $uid, $updateFields);
7757  }
7758  } elseif ($doExec) {
7759  return $db->exec_DELETEquery($table, 'uid=' . $uid);
7760  } else {
7761  return $db->DELETEquery($table, 'uid=' . $uid);
7762  }
7763  }
7764 
7779  public function DBgetUpdate($table, $uid, $dataArr, $fieldList, $doExec = false)
7780  {
7781  // uid can never be set
7782  unset($dataArr['uid']);
7783  $uid = (int)$uid;
7784  if ($uid) {
7785  $fieldList = implode(',', GeneralUtility::trimExplode(',', $fieldList, true));
7786  $updateFields = [];
7787  foreach ($dataArr as $f => $v) {
7788  if (GeneralUtility::inList($fieldList, $f)) {
7789  $updateFields[$f] = $v;
7790  }
7791  }
7792  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7793  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
7794  }
7795  if (!empty($updateFields)) {
7796  if ($doExec) {
7797  return $this->getDatabaseConnection()->exec_UPDATEquery($table, 'uid=' . $uid, $updateFields);
7798  }
7799  return $this->getDatabaseConnection()->UPDATEquery($table, 'uid=' . $uid, $updateFields);
7800  }
7801  }
7802  return '';
7803  }
7804 
7819  public function DBgetInsert($table, $pid, $dataArr, $fieldList, $doExec = false)
7820  {
7821  $extraList = 'pid';
7822  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7823  $field = $GLOBALS['TCA'][$table]['ctrl']['tstamp'];
7824  $dataArr[$field] = $GLOBALS['EXEC_TIME'];
7825  $extraList .= ',' . $field;
7826  }
7827  if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
7828  $field = $GLOBALS['TCA'][$table]['ctrl']['crdate'];
7829  $dataArr[$field] = $GLOBALS['EXEC_TIME'];
7830  $extraList .= ',' . $field;
7831  }
7832  if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
7833  $field = $GLOBALS['TCA'][$table]['ctrl']['cruser_id'];
7834  $dataArr[$field] = 0;
7835  $extraList .= ',' . $field;
7836  }
7837  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7838  $field = $GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id'];
7839  $dataArr[$field] = (int)$this->getTypoScriptFrontendController()->fe_user->user['uid'];
7840  $extraList .= ',' . $field;
7841  }
7842  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7843  $field = $GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id'];
7844  list($dataArr[$field]) = explode(',', $this->getTypoScriptFrontendController()->fe_user->user['usergroup']);
7845  $dataArr[$field] = (int)$dataArr[$field];
7846  $extraList .= ',' . $field;
7847  }
7848  // Uid can never be set
7849  unset($dataArr['uid']);
7850  if ($pid >= 0) {
7851  $dataArr['pid'] = $pid;
7852  }
7853  // Set pid < 0 and the dataarr-pid will be used!
7854  $fieldList = implode(',', GeneralUtility::trimExplode(',', $fieldList . ',' . $extraList, true));
7855  $insertFields = [];
7856  foreach ($dataArr as $f => $v) {
7857  if (GeneralUtility::inList($fieldList, $f)) {
7858  $insertFields[$f] = $v;
7859  }
7860  }
7861  if ($doExec) {
7862  return $this->getDatabaseConnection()->exec_INSERTquery($table, $insertFields);
7863  } else {
7864  return $this->getDatabaseConnection()->INSERTquery($table, $insertFields);
7865  }
7866  }
7867 
7879  public function DBmayFEUserEdit($table, $row, $feUserRow, $allowedGroups = '', $feEditSelf = 0)
7880  {
7881  if ($allowedGroups) {
7882  $groupList = implode(
7883  ',',
7884  array_intersect(
7885  GeneralUtility::trimExplode(',', $feUserRow['usergroup'], true),
7886  GeneralUtility::trimExplode(',', $allowedGroups, true)
7887  )
7888  );
7889  } else {
7890  $groupList = $feUserRow['usergroup'];
7891  }
7892  $ok = 0;
7893  // Points to the field that allows further editing from frontend if not set. If set the record is locked.
7894  if (!$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock'] || !$row[$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock']]) {
7895  // Points to the field (int) that holds the fe_users-id of the creator fe_user
7896  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7897  $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']];
7898  if ($rowFEUser && $rowFEUser === (int)$feUserRow['uid']) {
7899  $ok = 1;
7900  }
7901  }
7902  // If $feEditSelf is set, fe_users may always edit them selves...
7903  if ($feEditSelf && $table === 'fe_users' && (int)$feUserRow['uid'] === (int)$row['uid']) {
7904  $ok = 1;
7905  }
7906  // Points to the field (int) that holds the fe_group-id of the creator fe_user's first group
7907  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7908  $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']];
7909  if ($rowFEUser) {
7910  if (GeneralUtility::inList($groupList, $rowFEUser)) {
7911  $ok = 1;
7912  }
7913  }
7914  }
7915  }
7916  return $ok;
7917  }
7918 
7931  public function DBmayFEUserEditSelect($table, $feUserRow, $allowedGroups = '', $feEditSelf = 0)
7932  {
7933  // Returns where-definition that selects user-editable records.
7934  if ($allowedGroups) {
7935  $groupList = implode(
7936  ',',
7937  array_intersect(
7938  GeneralUtility::trimExplode(',', $feUserRow['usergroup'], true),
7939  GeneralUtility::trimExplode(',', $allowedGroups, true)
7940  )
7941  );
7942  } else {
7943  $groupList = $feUserRow['usergroup'];
7944  }
7945  $OR_arr = [];
7946  // Points to the field (int) that holds the fe_users-id of the creator fe_user
7947  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7948  $OR_arr[] = $GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id'] . '=' . $feUserRow['uid'];
7949  }
7950  // Points to the field (int) that holds the fe_group-id of the creator fe_user's first group
7951  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7952  $values = GeneralUtility::intExplode(',', $groupList);
7953  foreach ($values as $theGroupUid) {
7954  if ($theGroupUid) {
7955  $OR_arr[] = $GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id'] . '=' . $theGroupUid;
7956  }
7957  }
7958  }
7959  // If $feEditSelf is set, fe_users may always edit them selves...
7960  if ($feEditSelf && $table === 'fe_users') {
7961  $OR_arr[] = 'uid=' . (int)$feUserRow['uid'];
7962  }
7963  $whereDef = ' AND 1=0';
7964  if (!empty($OR_arr)) {
7965  $whereDef = ' AND (' . implode(' OR ', $OR_arr) . ')';
7966  if ($GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock']) {
7967  $whereDef .= ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock'] . '=0';
7968  }
7969  }
7970  return $whereDef;
7971  }
7972 
7988  public function enableFields($table, $show_hidden = false, array $ignore_array = [])
7989  {
7990  $tsfe = $this->getTypoScriptFrontendController();
7991  $show_hidden = $show_hidden ?: ($table === 'pages' ? $tsfe->showHiddenPage : $tsfe->showHiddenRecords);
7992  return $tsfe->sys_page->enableFields($table, (bool)$show_hidden, $ignore_array);
7993  }
7994 
8021  public function getTreeList($id, $depth, $begin = 0, $dontCheckEnableFields = false, $addSelectFields = '', $moreWhereClauses = '', array $prevId_array = [], $recursionLevel = 0)
8022  {
8023  $id = (int)$id;
8024  if (!$id) {
8025  return '';
8026  }
8027 
8028  // Init vars:
8029  $allFields = 'uid,hidden,starttime,endtime,fe_group,extendToSubpages,doktype,php_tree_stop,mount_pid,mount_pid_ol,t3ver_state' . $addSelectFields;
8030  $depth = (int)$depth;
8031  $begin = (int)$begin;
8032  $theList = [];
8033  $addId = 0;
8034  $requestHash = '';
8035 
8036  // First level, check id (second level, this is done BEFORE the recursive call)
8037  $db = $this->getDatabaseConnection();
8038  $tsfe = $this->getTypoScriptFrontendController();
8039  if (!$recursionLevel) {
8040  // Check tree list cache
8041  // First, create the hash for this request - not sure yet whether we need all these parameters though
8042  $parameters = [
8043  $id,
8044  $depth,
8045  $begin,
8046  $dontCheckEnableFields,
8047  $addSelectFields,
8048  $moreWhereClauses,
8049  $prevId_array,
8050  $tsfe->gr_list
8051  ];
8052  $requestHash = md5(serialize($parameters));
8053  $cacheEntry = $db->exec_SELECTgetSingleRow(
8054  'treelist',
8055  'cache_treelist',
8056  'md5hash = \'' . $requestHash . '\' AND ( expires > ' . (int)$GLOBALS['EXEC_TIME'] . ' OR expires = 0 )'
8057  );
8058  if (is_array($cacheEntry)) {
8059  // Cache hit
8060  return $cacheEntry['treelist'];
8061  }
8062  // If Id less than zero it means we should add the real id to list:
8063  if ($id < 0) {
8064  $addId = $id = abs($id);
8065  }
8066  // Check start page:
8067  if ($tsfe->sys_page->getRawRecord('pages', $id, 'uid')) {
8068  // Find mount point if any:
8069  $mount_info = $tsfe->sys_page->getMountPointInfo($id);
8070  if (is_array($mount_info)) {
8071  $id = $mount_info['mount_pid'];
8072  // In Overlay mode, use the mounted page uid as added ID!:
8073  if ($addId && $mount_info['overlay']) {
8074  $addId = $id;
8075  }
8076  }
8077  } else {
8078  // Return blank if the start page was NOT found at all!
8079  return '';
8080  }
8081  }
8082  // Add this ID to the array of IDs
8083  if ($begin <= 0) {
8084  $prevId_array[] = $id;
8085  }
8086  // Select sublevel:
8087  if ($depth > 0) {
8088  $rows = $db->exec_SELECTgetRows(
8089  $allFields,
8090  'pages',
8091  'pid = ' . (int)$id . ' AND deleted = 0 ' . $moreWhereClauses,
8092  '',
8093  'sorting'
8094  );
8095  if (is_array($rows)) {
8096  foreach ($rows as $row) {
8098  $versionState = VersionState::cast($row['t3ver_state']);
8099  $tsfe->sys_page->versionOL('pages', $row);
8100  if ((int)$row['doktype'] === PageRepository::DOKTYPE_RECYCLER
8101  || (int)$row['doktype'] === PageRepository::DOKTYPE_BE_USER_SECTION
8102  || $versionState->indicatesPlaceholder()
8103  ) {
8104  // Doing this after the overlay to make sure changes
8105  // in the overlay are respected.
8106  // However, we do not process pages below of and
8107  // including of type recycler and BE user section
8108  continue;
8109  }
8110  // Find mount point if any:
8111  $next_id = $row['uid'];
8112  $mount_info = $tsfe->sys_page->getMountPointInfo($next_id, $row);
8113  // Overlay mode:
8114  if (is_array($mount_info) && $mount_info['overlay']) {
8115  $next_id = $mount_info['mount_pid'];
8116  $row = $db->exec_SELECTgetSingleRow(
8117  $allFields,
8118  'pages',
8119  'uid = ' . (int)$next_id . ' AND deleted = 0 ' . $moreWhereClauses,
8120  '',
8121  'sorting'
8122  );
8123  $tsfe->sys_page->versionOL('pages', $row);
8124  if ((int)$row['doktype'] === PageRepository::DOKTYPE_RECYCLER
8125  || (int)$row['doktype'] === PageRepository::DOKTYPE_BE_USER_SECTION
8126  || $versionState->indicatesPlaceholder()
8127  ) {
8128  // Doing this after the overlay to make sure
8129  // changes in the overlay are respected.
8130  // see above
8131  continue;
8132  }
8133  }
8134  // Add record:
8135  if ($dontCheckEnableFields || $tsfe->checkPagerecordForIncludeSection($row)) {
8136  // Add ID to list:
8137  if ($begin <= 0) {
8138  if ($dontCheckEnableFields || $tsfe->checkEnableFields($row)) {
8139  $theList[] = $next_id;
8140  }
8141  }
8142  // Next level:
8143  if ($depth > 1 && !$row['php_tree_stop']) {
8144  // Normal mode:
8145  if (is_array($mount_info) && !$mount_info['overlay']) {
8146  $next_id = $mount_info['mount_pid'];
8147  }
8148  // Call recursively, if the id is not in prevID_array:
8149  if (!in_array($next_id, $prevId_array)) {
8150  $theList = array_merge(
8151  GeneralUtility::intExplode(
8152  ',',
8153  $this->getTreeList($next_id, $depth - 1, $begin - 1,
8154  $dontCheckEnableFields, $addSelectFields, $moreWhereClauses,
8155  $prevId_array, $recursionLevel + 1),
8156  true
8157  ),
8158  $theList
8159  );
8160  }
8161  }
8162  }
8163  }
8164  }
8165  }
8166  // If first run, check if the ID should be returned:
8167  if (!$recursionLevel) {
8168  if ($addId) {
8169  if ($begin > 0) {
8170  $theList[] = 0;
8171  } else {
8172  $theList[] = $addId;
8173  }
8174  }
8175  $db->exec_INSERTquery('cache_treelist', [
8176  'md5hash' => $requestHash,
8177  'pid' => $id,
8178  'treelist' => implode(',', $theList),
8179  'tstamp' => $GLOBALS['EXEC_TIME']
8180  ]);
8181  }
8182 
8183  return implode(',', $theList);
8184  }
8185 
8200  public function exec_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
8201  {
8202  return $this->getDatabaseConnection()->exec_SELECTquery(
8203  $select,
8204  $local_table . ',' . $mm_table . ($foreign_table ? ',' . $foreign_table : ''),
8205  $local_table . '.uid=' . $mm_table . '.uid_local'
8206  . ($foreign_table ? ' AND ' . $foreign_table . '.uid=' . $mm_table . '.uid_foreign' : '')
8207  . $whereClause,
8208  $groupBy,
8209  $orderBy,
8210  $limit
8211  );
8212  }
8213 
8229  public function exec_mm_query_uidList($select, $local_table_uidlist, $mm_table, $foreign_table = '', $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
8230  {
8231  return $this->getDatabaseConnection()->exec_SELECTquery(
8232  $select,
8233  $mm_table . ($foreign_table ? ',' . $foreign_table : ''),
8234  $mm_table . '.uid_local IN (' . $local_table_uidlist . ')'
8235  . ($foreign_table ? ' AND ' . $foreign_table . '.uid=' . $mm_table . '.uid_foreign' : '')
8236  . $whereClause,
8237  $groupBy,
8238  $orderBy,
8239  $limit
8240  );
8241  }
8242 
8252  public function searchWhere($sw, $searchFieldList, $searchTable = '')
8253  {
8254  $prefixTableName = $searchTable ? $searchTable . '.' : '';
8255  $where = '';
8256  if ($sw) {
8257  $searchFields = explode(',', $searchFieldList);
8258  $kw = preg_split('/[ ,]/', $sw);
8259  $db = $this->getDatabaseConnection();
8260  foreach ($kw as $val) {
8261  $val = trim($val);
8262  $where_p = [];
8263  if (strlen($val) >= 2) {
8264  $val = $db->escapeStrForLike($db->quoteStr($val, $searchTable), $searchTable);
8265  foreach ($searchFields as $field) {
8266  $where_p[] = $prefixTableName . $field . ' LIKE \'%' . $val . '%\'';
8267  }
8268  }
8269  if (!empty($where_p)) {
8270  $where .= ' AND (' . implode(' OR ', $where_p) . ')';
8271  }
8272  }
8273  }
8274  return $where;
8275  }
8276 
8286  public function exec_getQuery($table, $conf)
8287  {
8288  $queryParts = $this->getQuery($table, $conf, true);
8289  return $this->getDatabaseConnection()->exec_SELECT_queryArray($queryParts);
8290  }
8291 
8300  public function getRecords($tableName, array $queryConfiguration)
8301  {
8302  $records = [];
8303 
8304  $res = $this->exec_getQuery($tableName, $queryConfiguration);
8305 
8306  $db = $this->getDatabaseConnection();
8307  if ($error = $db->sql_error()) {
8308  $this->getTimeTracker()->setTSlogMessage($error, 3);
8309  } else {
8310  $tsfe = $this->getTypoScriptFrontendController();
8311  while (($row = $db->sql_fetch_assoc($res)) !== false) {
8312 
8313  // Versioning preview:
8314  $tsfe->sys_page->versionOL($tableName, $row, true);
8315 
8316  // Language overlay:
8317  if (is_array($row) && $tsfe->sys_language_contentOL) {
8318  if ($tableName === 'pages') {
8319  $row = $tsfe->sys_page->getPageOverlay($row);
8320  } else {
8321  $row = $tsfe->sys_page->getRecordOverlay(
8322  $tableName,
8323  $row,
8324  $tsfe->sys_language_content,
8325  $tsfe->sys_language_contentOL
8326  );
8327  }
8328  }
8329 
8330  // Might be unset in the sys_language_contentOL
8331  if (is_array($row)) {
8332  $records[] = $row;
8333  }
8334  }
8335  $db->sql_free_result($res);
8336  }
8337 
8338  return $records;
8339  }
8340 
8352  public function getQuery($table, $conf, $returnQueryArray = false)
8353  {
8354  // Resolve stdWrap in these properties first
8355  $properties = [
8356  'pidInList',
8357  'uidInList',
8358  'languageField',
8359  'selectFields',
8360  'max',
8361  'begin',
8362  'groupBy',
8363  'orderBy',
8364  'join',
8365  'leftjoin',
8366  'rightjoin',
8367  'recursive',
8368  'where'
8369  ];
8370  foreach ($properties as $property) {
8371  $conf[$property] = trim(isset($conf[$property . '.'])
8372  ? $this->stdWrap($conf[$property], $conf[$property . '.'])
8373  : $conf[$property]
8374  );
8375  if ($conf[$property] === '') {
8376  unset($conf[$property]);
8377  }
8378  if (isset($conf[$property . '.'])) {
8379  // stdWrapping already done, so remove the sub-array
8380  unset($conf[$property . '.']);
8381  }
8382  }
8383  // Handle PDO-style named parameter markers first
8384  $queryMarkers = $this->getQueryMarkers($table, $conf);
8385  // Replace the markers in the non-stdWrap properties
8386  foreach ($queryMarkers as $marker => $markerValue) {
8387  $properties = [
8388  'uidInList',
8389  'selectFields',
8390  'where',
8391  'max',
8392  'begin',
8393  'groupBy',
8394  'orderBy',
8395  'join',
8396  'leftjoin',
8397  'rightjoin'
8398  ];
8399  foreach ($properties as $property) {
8400  if ($conf[$property]) {
8401  $conf[$property] = str_replace('###' . $marker . '###', $markerValue, $conf[$property]);
8402  }
8403  }
8404  }
8405  // Construct WHERE clause:
8406  // Handle recursive function for the pidInList
8407  if (isset($conf['recursive'])) {
8408  $conf['recursive'] = (int)$conf['recursive'];
8409  if ($conf['recursive'] > 0) {
8410  $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true);
8411  array_walk($pidList, function (&$storagePid) {
8412  if ($storagePid === 'this') {
8413  $storagePid = $this->getTypoScriptFrontendController()->id;
8414  }
8415  if ($storagePid > 0) {
8416  $storagePid = -$storagePid;
8417  }
8418  });
8419  $expandedPidList = [];
8420  foreach ($pidList as $value) {
8421  // Implementation of getTreeList allows to pass the id negative to include
8422  // it into the result otherwise only childpages are returned
8423  $expandedPidList = array_merge(
8424  GeneralUtility::intExplode(',', $this->getTreeList($value, $conf['recursive'])),
8425  $expandedPidList
8426  );
8427  }
8428  $conf['pidInList'] = implode(',', $expandedPidList);
8429  }
8430  }
8431  if ((string)$conf['pidInList'] === '') {
8432  $conf['pidInList'] = 'this';
8433  }
8434  $queryParts = $this->getWhere($table, $conf, true);
8435  // Fields:
8436  if ($conf['selectFields']) {
8437  $queryParts['SELECT'] = $this->sanitizeSelectPart($conf['selectFields'], $table);
8438  } else {
8439  $queryParts['SELECT'] = '*';
8440  }
8441  // Setting LIMIT:
8442  $db = $this->getDatabaseConnection();
8443  $error = 0;
8444  if ($conf['max'] || $conf['begin']) {
8445  // Finding the total number of records, if used:
8446  if (strstr(strtolower($conf['begin'] . $conf['max']), 'total')) {
8447  $res = $db->exec_SELECTquery('count(*)', $table, $queryParts['WHERE'], $queryParts['GROUPBY']);
8448  if ($error = $db->sql_error()) {
8449  $this->getTimeTracker()->setTSlogMessage($error);
8450  } else {
8451  $row = $db->sql_fetch_row($res);
8452  $conf['max'] = str_ireplace('total', $row[0], $conf['max']);
8453  $conf['begin'] = str_ireplace('total', $row[0], $conf['begin']);
8454  }
8455  $db->sql_free_result($res);
8456  }
8457  if (!$error) {
8458  $conf['begin'] = MathUtility::forceIntegerInRange(ceil($this->calc($conf['begin'])), 0);
8459  $conf['max'] = MathUtility::forceIntegerInRange(ceil($this->calc($conf['max'])), 0);
8460  if ($conf['begin'] && !$conf['max']) {
8461  $conf['max'] = 100000;
8462  }
8463  if ($conf['begin'] && $conf['max']) {
8464  $queryParts['LIMIT'] = $conf['begin'] . ',' . $conf['max'];
8465  } elseif (!$conf['begin'] && $conf['max']) {
8466  $queryParts['LIMIT'] = $conf['max'];
8467  }
8468  }
8469  }
8470  if (!$error) {
8471  // Setting up tablejoins:
8472  $joinPart = '';
8473  if ($conf['join']) {
8474  $joinPart = 'JOIN ' . $conf['join'];
8475  } elseif ($conf['leftjoin']) {
8476  $joinPart = 'LEFT OUTER JOIN ' . $conf['leftjoin'];
8477  } elseif ($conf['rightjoin']) {
8478  $joinPart = 'RIGHT OUTER JOIN ' . $conf['rightjoin'];
8479  }
8480  // Compile and return query:
8481  $queryParts['FROM'] = trim($table . ' ' . $joinPart);
8482  // Replace the markers in the queryParts to handle stdWrap
8483  // enabled properties
8484  foreach ($queryMarkers as $marker => $markerValue) {
8485  foreach ($queryParts as $queryPartKey => &$queryPartValue) {
8486  $queryPartValue = str_replace('###' . $marker . '###', $markerValue, $queryPartValue);
8487  }
8488  unset($queryPartValue);
8489  }
8490  $query = $db->SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
8491  return $returnQueryArray ? $queryParts : $query;
8492  }
8493  return '';
8494  }
8495 
8506  public function getWhere($table, $conf, $returnQueryArray = false)
8507  {
8508  // Init:
8509  $query = '';
8510  $pid_uid_flag = 0;
8511  $enableFieldsIgnore = [];
8512  $queryParts = [
8513  'SELECT' => '',
8514  'FROM' => '',
8515  'WHERE' => '',
8516  'GROUPBY' => '',
8517  'ORDERBY' => '',
8518  'LIMIT' => ''
8519  ];
8520  $tsfe = $this->getTypoScriptFrontendController();
8521  $considerMovePlaceholders = (
8522  $tsfe->sys_page->versioningPreview && $table !== 'pages'
8523  && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
8524  );
8525  if (trim($conf['uidInList'])) {
8526  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['uidInList']));
8527  if (count($listArr) === 1) {
8528  $comparison = '=' . (int)$listArr[0];
8529  } else {
8530  $comparison = ' IN (' . implode(',', $this->getDatabaseConnection()->cleanIntArray($listArr)) . ')';
8531  }
8532  // If move placeholder shall be considered, select via t3ver_move_id
8533  if ($considerMovePlaceholders) {
8534  $movePlaceholderComparison = $table . '.t3ver_state=' . VersionState::cast(VersionState::MOVE_PLACEHOLDER) . ' AND ' . $table . '.t3ver_move_id' . $comparison;
8535  $query .= ' AND (' . $table . '.uid' . $comparison . ' OR ' . $movePlaceholderComparison . ')';
8536  } else {
8537  $query .= ' AND ' . $table . '.uid' . $comparison;
8538  }
8539  $pid_uid_flag++;
8540  }
8541  // Static_* tables are allowed to be fetched from root page
8542  if (substr($table, 0, 7) === 'static_') {
8543  $pid_uid_flag++;
8544  }
8545  if (trim($conf['pidInList'])) {
8546  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['pidInList']));
8547  // Removes all pages which are not visible for the user!
8548  $listArr = $this->checkPidArray($listArr);
8549  if (GeneralUtility::inList($conf['pidInList'], 'root')) {
8550  $listArr[] = 0;
8551  }
8552  if (GeneralUtility::inList($conf['pidInList'], '-1')) {
8553  $listArr[] = -1;
8554  $enableFieldsIgnore['pid'] = true;
8555  }
8556  if (!empty($listArr)) {
8557  $query .= ' AND ' . $table . '.pid IN (' . implode(',', array_map('intval', $listArr)) . ')';
8558  $pid_uid_flag++;
8559  } else {
8560  // If not uid and not pid then uid is set to 0 - which results in nothing!!
8561  $pid_uid_flag = 0;
8562  }
8563  }
8564  // If not uid and not pid then uid is set to 0 - which results in nothing!!
8565  if (!$pid_uid_flag) {
8566  $query .= ' AND ' . $table . '.uid=0';
8567  }
8568  $where = isset($conf['where.']) ? trim($this->stdWrap($conf['where'], $conf['where.'])) : trim($conf['where']);
8569  if ($where) {
8570  $query .= ' AND ' . $where;
8571  }
8572 
8573  // Check if the table is translatable, and set the language field by default from the TCA information
8574  $languageField = '';
8575  if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
8576  if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
8577  $languageField = $conf['languageField'];
8578  } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
8579  $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
8580  }
8581  }
8582 
8583  if (!empty($languageField)) {
8584  // The sys_language record UID of the content of the page
8585  $sys_language_content = (int)$tsfe->sys_language_content;
8586 
8587  if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
8588  // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
8589  // OVERLAY the records with localized versions!
8590  $languageQuery = $languageField . ' IN (0,-1)';
8591  // Use this option to include records that don't have a default translation
8592  // (originalpointerfield is 0 and the language field contains the requested language)
8593  $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ?
8594  $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) :
8595  $conf['includeRecordsWithoutDefaultTranslation'];
8596  if (!empty(trim($includeRecordsWithoutDefaultTranslation))) {
8597  $languageQuery .= ' OR (' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ' = 0 AND ' .
8598  $languageField . ' = ' . $sys_language_content . ')';
8599  }
8600  } else {
8601  $languageQuery = $languageField . ' = ' . $sys_language_content;
8602  }
8603  $query .= ' AND (' . $languageQuery . ')';
8604  }
8605  $andWhere = isset($conf['andWhere.']) ? trim($this->stdWrap($conf['andWhere'], $conf['andWhere.'])) : trim($conf['andWhere']);
8606  if ($andWhere) {
8607  GeneralUtility::deprecationLog('Usage of TypoScript property "andWhere" is deprecated since 7.1 in favor of "where". It has been used to query the table "' . $table . '".');
8608  $query .= ' AND ' . $andWhere;
8609  }
8610  // Enablefields
8611  if ($table === 'pages') {
8612  $query .= ' ' . $tsfe->sys_page->where_hid_del . $tsfe->sys_page->where_groupAccess;
8613  } else {
8614  $query .= $this->enableFields($table, false, $enableFieldsIgnore);
8615  }
8616  // MAKE WHERE:
8617  if ($query) {
8618  // Stripping of " AND"...
8619  $queryParts['WHERE'] = trim(substr($query, 4));
8620  $query = 'WHERE ' . $queryParts['WHERE'];
8621  }
8622  // GROUP BY
8623  if (trim($conf['groupBy'])) {
8624  $queryParts['GROUPBY'] = isset($conf['groupBy.']) ? trim($this->stdWrap($conf['groupBy'], $conf['groupBy.'])) : trim($conf['groupBy']);
8625  $query .= ' GROUP BY ' . $queryParts['GROUPBY'];
8626  }
8627  // ORDER BY
8628  if (trim($conf['orderBy'])) {
8629  $queryParts['ORDERBY'] = isset($conf['orderBy.']) ? trim($this->stdWrap($conf['orderBy'], $conf['orderBy.'])) : trim($conf['orderBy']);
8630  $query .= ' ORDER BY ' . $queryParts['ORDERBY'];
8631  }
8632  // Return result:
8633  return $returnQueryArray ? $queryParts : $query;
8634  }
8635 
8648  protected function sanitizeSelectPart($selectPart, $table)
8649  {
8650  // Pattern matching parts
8651  $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)';
8652  $matchEnd = '(\\s*,|\\s*$)/';
8653  $necessaryFields = ['uid', 'pid'];
8654  $wsFields = ['t3ver_state'];
8655  if (isset($GLOBALS['TCA'][$table]) && !preg_match(($matchStart . '\\*' . $matchEnd), $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)/i', $selectPart)) {
8656  foreach ($necessaryFields as $field) {
8657  $match = $matchStart . $field . $matchEnd;
8658  if (!preg_match($match, $selectPart)) {
8659  $selectPart .= ', ' . $table . '.' . $field . ' as ' . $field;
8660  }
8661  }
8662  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
8663  foreach ($wsFields as $field) {
8664  $match = $matchStart . $field . $matchEnd;
8665  if (!preg_match($match, $selectPart)) {
8666  $selectPart .= ', ' . $table . '.' . $field . ' as ' . $field;
8667  }
8668  }
8669  }
8670  }
8671  return $selectPart;
8672  }
8673 
8682  public function checkPidArray($listArr)
8683  {
8684  if (!is_array($listArr) || empty($listArr)) {
8685  return [];
8686  }
8687  $outArr = [];
8688  $db = $this->getDatabaseConnection();
8689  $res = $db->exec_SELECTquery('uid', 'pages', 'uid IN (' . implode(',', $listArr) . ')' . $this->enableFields('pages') . ' AND doktype NOT IN (' . $this->checkPid_badDoktypeList . ')');
8690  if ($error = $db->sql_error()) {
8691  $this->getTimeTracker()->setTSlogMessage($error . ': ' . $db->debug_lastBuiltQuery, 3);
8692  } else {
8693  while ($row = $db->sql_fetch_assoc($res)) {
8694  $outArr[] = $row['uid'];
8695  }
8696  }
8697  $db->sql_free_result($res);
8698  return $outArr;
8699  }
8700 
8709  public function checkPid($uid)
8710  {
8711  $uid = (int)$uid;
8712  if (!isset($this->checkPid_cache[$uid])) {
8713  $count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'pages', 'uid=' . $uid . $this->enableFields('pages') . ' AND doktype NOT IN (' . $this->checkPid_badDoktypeList . ')');
8714  $this->checkPid_cache[$uid] = (bool)$count;
8715  }
8716  return $this->checkPid_cache[$uid];
8717  }
8718 
8729  public function getQueryMarkers($table, $conf)
8730  {
8731  if (!is_array($conf['markers.'])) {
8732  return [];
8733  }
8734  // Parse markers and prepare their values
8735  $db = $this->getDatabaseConnection();
8736  $markerValues = [];
8737  foreach ($conf['markers.'] as $dottedMarker => $dummy) {
8738  $marker = rtrim($dottedMarker, '.');
8739  if ($dottedMarker != $marker . '.') {
8740  continue;
8741  }
8742  // Parse definition
8743  $tempValue = isset($conf['markers.'][$dottedMarker])
8744  ? $this->stdWrap($conf['markers.'][$dottedMarker]['value'], $conf['markers.'][$dottedMarker])
8745  : $conf['markers.'][$dottedMarker]['value'];
8746  // Quote/escape if needed
8747  if (is_numeric($tempValue)) {
8748  if ((int)$tempValue == $tempValue) {
8749  // Handle integer
8750  $markerValues[$marker] = (int)$tempValue;
8751  } else {
8752  // Handle float
8753  $markerValues[$marker] = floatval($tempValue);
8754  }
8755  } elseif (is_null($tempValue)) {
8756  // It represents NULL
8757  $markerValues[$marker] = 'NULL';
8758  } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) {
8759  // See if it is really a comma separated list of values
8760  $explodeValues = GeneralUtility::trimExplode(',', $tempValue);
8761  if (count($explodeValues) > 1) {
8762  // Handle each element of list separately
8763  $tempArray = [];
8764  foreach ($explodeValues as $listValue) {
8765  if (is_numeric($listValue)) {
8766  if ((int)$listValue == $listValue) {
8767  $tempArray[] = (int)$listValue;
8768  } else {
8769  $tempArray[] = floatval($listValue);
8770  }
8771  } else {
8772  // If quoted, remove quotes before
8773  // escaping.
8774  if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) {
8775  $listValue = $matches[1];
8776  } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) {
8777  $listValue = $matches[1];
8778  }
8779  $tempArray[] = $db->fullQuoteStr($listValue, $table);
8780  }
8781  }
8782  $markerValues[$marker] = implode(',', $tempArray);
8783  } else {
8784  // Handle remaining values as string
8785  $markerValues[$marker] = $db->fullQuoteStr($tempValue, $table);
8786  }
8787  } else {
8788  // Handle remaining values as string
8789  $markerValues[$marker] = $db->fullQuoteStr($tempValue, $table);
8790  }
8791  }
8792  return $markerValues;
8793  }
8794 
8795  /***********************************************
8796  *
8797  * Frontend editing functions
8798  *
8799  ***********************************************/
8811  public function editPanel($content, $conf, $currentRecord = '', $dataArr = [])
8812  {
8813  if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
8814  if (!$currentRecord) {
8816  }
8817  if (empty($dataArr)) {
8818  $dataArr = $this->data;
8819  }
8820  // Delegate rendering of the edit panel to the frontend edit
8821  $content = $this->getFrontendBackendUser()->frontendEdit->displayEditPanel($content, $conf, $currentRecord, $dataArr);
8822  }
8823  return $content;
8824  }
8825 
8838  public function editIcons($content, $params, array $conf = [], $currentRecord = '', $dataArr = [], $addUrlParamStr = '')
8839  {
8840  if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
8841  if (!$currentRecord) {
8843  }
8844  if (empty($dataArr)) {
8845  $dataArr = $this->data;
8846  }
8847  // Delegate rendering of the edit panel to frontend edit class.
8848  $content = $this->getFrontendBackendUser()->frontendEdit->displayEditIcons($content, $params, $conf, $currentRecord, $dataArr, $addUrlParamStr);
8849  }
8850  return $content;
8851  }
8852 
8862  public function isDisabled($table, $row)
8863  {
8864  $tsfe = $this->getTypoScriptFrontendController();
8865  $enablecolumns = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
8866  return $enablecolumns['disabled'] && $row[$enablecolumns['disabled']]
8867  || $enablecolumns['fe_group'] && $tsfe->simUserGroup && (int)$row[$enablecolumns['fe_group']] === (int)$tsfe->simUserGroup
8868  || $enablecolumns['starttime'] && $row[$enablecolumns['starttime']] > $GLOBALS['EXEC_TIME']
8869  || $enablecolumns['endtime'] && $row[$enablecolumns['endtime']] && $row[$enablecolumns['endtime']] < $GLOBALS['EXEC_TIME'];
8870  }
8871 
8877  protected function getResourceFactory()
8878  {
8880  }
8881 
8889  protected function getEnvironmentVariable($key)
8890  {
8891  return GeneralUtility::getIndpEnv($key);
8892  }
8893 
8901  protected function getFromCache(array $configuration)
8902  {
8903  $content = false;
8904 
8905  $cacheKey = $this->calculateCacheKey($configuration);
8906  if (!empty($cacheKey)) {
8908  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)
8909  ->getCache('cache_hash');
8910  $content = $cacheFrontend->get($cacheKey);
8911  }
8912  return $content;
8913  }
8914 
8921  protected function calculateCacheLifetime(array $configuration)
8922  {
8923  $lifetimeConfiguration = isset($configuration['lifetime'])
8924  ? $configuration['lifetime']
8925  : '';
8926  $lifetimeConfiguration = isset($configuration['lifetime.'])
8927  ? $this->stdWrap($lifetimeConfiguration, $configuration['lifetime.'])
8928  : $lifetimeConfiguration;
8929 
8930  $lifetime = null; // default lifetime
8931  if (strtolower($lifetimeConfiguration) === 'unlimited') {
8932  $lifetime = 0; // unlimited
8933  } elseif ($lifetimeConfiguration > 0) {
8934  $lifetime = (int)$lifetimeConfiguration; // lifetime in seconds
8935  }
8936  return $lifetime;
8937  }
8938 
8945  protected function calculateCacheTags(array $configuration)
8946  {
8947  $tags = isset($configuration['tags']) ? $configuration['tags'] : '';
8948  $tags = isset($configuration['tags.'])
8949  ? $this->stdWrap($tags, $configuration['tags.'])
8950  : $tags;
8951  return empty($tags) ? [] : GeneralUtility::trimExplode(',', $tags);
8952  }
8953 
8960  protected function calculateCacheKey(array $configuration)
8961  {
8962  $key = isset($configuration['key']) ? $configuration['key'] : '';
8963  return isset($configuration['key.'])
8964  ? $this->stdWrap($key, $configuration['key.'])
8965  : $key;
8966  }
8967 
8973  protected function getFrontendBackendUser()
8974  {
8975  return $GLOBALS['BE_USER'];
8976  }
8977 
8983  protected function getDatabaseConnection()
8984  {
8985  return $GLOBALS['TYPO3_DB'];
8986  }
8987 
8991  protected function getTimeTracker()
8992  {
8993  return $GLOBALS['TT'];
8994  }
8995 
8999  protected function getTypoScriptFrontendController()
9000  {
9001  return $this->typoScriptFrontendController ?: $GLOBALS['TSFE'];
9002  }
9003 }
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
if(!defined("DB_ERROR")) define("DB_ERROR"
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration=[])
editIcons($content, $params, array $conf=[], $currentRecord='', $dataArr=[], $addUrlParamStr='')
debug($variable='', $name=' *variable *', $line=' *line *', $file=' *file *', $recursiveDepth=3, $debugLevel='E_DEBUG')
static isFirstPartOfStr($str, $partStr)
substituteMarkerArray($content, array $markContentArray, $wrap='', $uppercase=false, $deleteUnused=false)
static forceIntegerInRange($theInt, $min, $max=2000000000, $defaultValue=0)
Definition: MathUtility.php:31
static sortedKeyList($setupArr, $acceptOnlyProperties=false)
rootLineValue($key, $field, $slideBack=false, $altRootLine='')
DBgetUpdate($table, $uid, $dataArr, $fieldList, $doExec=false)
static hmac($input, $additionalSecret='')
DBmayFEUserEditSelect($table, $feUserRow, $allowedGroups='', $feEditSelf=0)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static calculateWithParentheses($string)
static callUserFunction($funcName, &$params, &$ref, $checkPrefix='', $errorMode=0)
static split_fileref($fileNameWithPath)
substituteMarkerArrayCached($content, array $markContentArray=null, array $subpartContentArray=null, array $wrappedSubpartContentArray=null)
DBmayFEUserEdit($table, $row, $feUserRow, $allowedGroups='', $feEditSelf=0)
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
enableFields($table, $show_hidden=false, array $ignore_array=[])
editPanel($content, $conf, $currentRecord='', $dataArr=[])
getQueryArguments($conf, $overruleQueryArguments=[], $forceOverruleArguments=false)
static explodeUrl2Array($string, $multidim=false)
static splitCalc($string, $operators)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
__construct(TypoScriptFrontendController $typoScriptFrontendController=null)
static xml2array($string, $NSprefix='', $reportDocTag=false)
$uid
Definition: server.php:38
getTypoLink($label, $params, $urlParameters=[], $target='')
static formatSize($sizeInBytes, $labels='', $base=0)
static revExplode($delimiter, $string, $count=0)
static beginsWith($haystack, $needle)
DBgetInsert($table, $pid, $dataArr, $fieldList, $doExec=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
getTypoLink_URL($params, $urlParameters=[], $target='')
static uniqueList($in_list, $secondParameter=null)
static arrayDiffAssocRecursive(array $array1, array $array2)
static endsWith($haystack, $needle)