TYPO3  7.6
ContentObjectRenderer.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
17 use TYPO3\CMS\Compatibility6\ContentObject\OffsetTableContentObject;
24 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
53 
64 {
68  public $align = array(
69  'center',
70  'right',
71  'left'
72  );
73 
79  public $stdWrapOrder = array(
80  'stdWrapPreProcess' => 'hook',
81  // this is a placeholder for the first Hook
82  'cacheRead' => 'hook',
83  // this is a placeholder for checking if the content is available in cache
84  'setContentToCurrent' => 'boolean',
85  'setContentToCurrent.' => 'array',
86  'addPageCacheTags' => 'string',
87  'addPageCacheTags.' => 'array',
88  'setCurrent' => 'string',
89  'setCurrent.' => 'array',
90  'lang.' => 'array',
91  'data' => 'getText',
92  'data.' => 'array',
93  'field' => 'fieldName',
94  'field.' => 'array',
95  'current' => 'boolean',
96  'current.' => 'array',
97  'cObject' => 'cObject',
98  'cObject.' => 'array',
99  'numRows.' => 'array',
100  'filelist' => 'dir',
101  'filelist.' => 'array',
102  'preUserFunc' => 'functionName',
103  'stdWrapOverride' => 'hook',
104  // this is a placeholder for the second Hook
105  'override' => 'string',
106  'override.' => 'array',
107  'preIfEmptyListNum' => 'listNum',
108  'preIfEmptyListNum.' => 'array',
109  'ifNull' => 'string',
110  'ifNull.' => 'array',
111  'ifEmpty' => 'string',
112  'ifEmpty.' => 'array',
113  'ifBlank' => 'string',
114  'ifBlank.' => 'array',
115  'listNum' => 'listNum',
116  'listNum.' => 'array',
117  'trim' => 'boolean',
118  'trim.' => 'array',
119  'strPad.' => 'array',
120  'stdWrap' => 'stdWrap',
121  'stdWrap.' => 'array',
122  'stdWrapProcess' => 'hook',
123  // this is a placeholder for the third Hook
124  'required' => 'boolean',
125  'required.' => 'array',
126  'if.' => 'array',
127  'fieldRequired' => 'fieldName',
128  'fieldRequired.' => 'array',
129  'csConv' => 'string',
130  'csConv.' => 'array',
131  'parseFunc' => 'objectpath',
132  'parseFunc.' => 'array',
133  'HTMLparser' => 'boolean',
134  'HTMLparser.' => 'array',
135  'split.' => 'array',
136  'replacement.' => 'array',
137  'prioriCalc' => 'boolean',
138  'prioriCalc.' => 'array',
139  'char' => 'integer',
140  'char.' => 'array',
141  'intval' => 'boolean',
142  'intval.' => 'array',
143  'hash' => 'string',
144  'hash.' => 'array',
145  'round' => 'boolean',
146  'round.' => 'array',
147  'numberFormat.' => 'array',
148  'expandList' => 'boolean',
149  'expandList.' => 'array',
150  'date' => 'dateconf',
151  'date.' => 'array',
152  'strtotime' => 'strtotimeconf',
153  'strtotime.' => 'array',
154  'strftime' => 'strftimeconf',
155  'strftime.' => 'array',
156  'age' => 'boolean',
157  'age.' => 'array',
158  'case' => 'case',
159  'case.' => 'array',
160  'bytes' => 'boolean',
161  'bytes.' => 'array',
162  'substring' => 'parameters',
163  'substring.' => 'array',
164  'removeBadHTML' => 'boolean',
165  'removeBadHTML.' => 'array',
166  'cropHTML' => 'crop',
167  'cropHTML.' => 'array',
168  'stripHtml' => 'boolean',
169  'stripHtml.' => 'array',
170  'crop' => 'crop',
171  'crop.' => 'array',
172  'rawUrlEncode' => 'boolean',
173  'rawUrlEncode.' => 'array',
174  'htmlSpecialChars' => 'boolean',
175  'htmlSpecialChars.' => 'array',
176  'encodeForJavaScriptValue' => 'boolean',
177  'encodeForJavaScriptValue.' => 'array',
178  'doubleBrTag' => 'string',
179  'doubleBrTag.' => 'array',
180  'br' => 'boolean',
181  'br.' => 'array',
182  'brTag' => 'string',
183  'brTag.' => 'array',
184  'encapsLines.' => 'array',
185  'keywords' => 'boolean',
186  'keywords.' => 'array',
187  'innerWrap' => 'wrap',
188  'innerWrap.' => 'array',
189  'innerWrap2' => 'wrap',
190  'innerWrap2.' => 'array',
191  'fontTag' => 'wrap',
192  'fontTag.' => 'array',
193  'addParams.' => 'array',
194  'textStyle.' => 'array',
195  'tableStyle.' => 'array',
196  'filelink.' => 'array',
197  'preCObject' => 'cObject',
198  'preCObject.' => 'array',
199  'postCObject' => 'cObject',
200  'postCObject.' => 'array',
201  'wrapAlign' => 'align',
202  'wrapAlign.' => 'array',
203  'typolink.' => 'array',
204  'TCAselectItem.' => 'array',
205  'space' => 'space',
206  'space.' => 'array',
207  'spaceBefore' => 'int',
208  'spaceBefore.' => 'array',
209  'spaceAfter' => 'int',
210  'spaceAfter.' => 'array',
211  'wrap' => 'wrap',
212  'wrap.' => 'array',
213  'noTrimWrap' => 'wrap',
214  'noTrimWrap.' => 'array',
215  'wrap2' => 'wrap',
216  'wrap2.' => 'array',
217  'dataWrap' => 'dataWrap',
218  'dataWrap.' => 'array',
219  'prepend' => 'cObject',
220  'prepend.' => 'array',
221  'append' => 'cObject',
222  'append.' => 'array',
223  'wrap3' => 'wrap',
224  'wrap3.' => 'array',
225  'orderedStdWrap' => 'stdWrap',
226  'orderedStdWrap.' => 'array',
227  'outerWrap' => 'wrap',
228  'outerWrap.' => 'array',
229  'insertData' => 'boolean',
230  'insertData.' => 'array',
231  'offsetWrap' => 'space',
232  'offsetWrap.' => 'array',
233  'postUserFunc' => 'functionName',
234  'postUserFuncInt' => 'functionName',
235  'prefixComment' => 'string',
236  'prefixComment.' => 'array',
237  'editIcons' => 'string',
238  'editIcons.' => 'array',
239  'editPanel' => 'boolean',
240  'editPanel.' => 'array',
241  'cacheStore' => 'hook',
242  // this is a placeholder for storing the content in cache
243  'stdWrapPostProcess' => 'hook',
244  // this is a placeholder for the last Hook
245  'debug' => 'boolean',
246  'debug.' => 'array',
247  'debugFunc' => 'boolean',
248  'debugFunc.' => 'array',
249  'debugData' => 'boolean',
250  'debugData.' => 'array'
251  );
252 
258  protected $contentObjectClassMap = array();
259 
266  public $image_compression = array(
267  10 => array(
268  'params' => '',
269  'ext' => 'gif'
270  ),
271  11 => array(
272  'params' => '-colors 128',
273  'ext' => 'gif'
274  ),
275  12 => array(
276  'params' => '-colors 64',
277  'ext' => 'gif'
278  ),
279  13 => array(
280  'params' => '-colors 32',
281  'ext' => 'gif'
282  ),
283  14 => array(
284  'params' => '-colors 16',
285  'ext' => 'gif'
286  ),
287  15 => array(
288  'params' => '-colors 8',
289  'ext' => 'gif'
290  ),
291  20 => array(
292  'params' => '-quality 100',
293  'ext' => 'jpg'
294  ),
295  21 => array(
296  'params' => '-quality 90',
297  'ext' => 'jpg'
298  ),
299  22 => array(
300  'params' => '-quality 80',
301  'ext' => 'jpg'
302  ),
303  23 => array(
304  'params' => '-quality 70',
305  'ext' => 'jpg'
306  ),
307  24 => array(
308  'params' => '-quality 60',
309  'ext' => 'jpg'
310  ),
311  25 => array(
312  'params' => '-quality 50',
313  'ext' => 'jpg'
314  ),
315  26 => array(
316  'params' => '-quality 40',
317  'ext' => 'jpg'
318  ),
319  27 => array(
320  'params' => '-quality 30',
321  'ext' => 'jpg'
322  ),
323  28 => array(
324  'params' => '-quality 20',
325  'ext' => 'jpg'
326  ),
327  30 => array(
328  'params' => '-colors 256',
329  'ext' => 'png'
330  ),
331  31 => array(
332  'params' => '-colors 128',
333  'ext' => 'png'
334  ),
335  32 => array(
336  'params' => '-colors 64',
337  'ext' => 'png'
338  ),
339  33 => array(
340  'params' => '-colors 32',
341  'ext' => 'png'
342  ),
343  34 => array(
344  'params' => '-colors 16',
345  'ext' => 'png'
346  ),
347  35 => array(
348  'params' => '-colors 8',
349  'ext' => 'png'
350  ),
351  39 => array(
352  'params' => '',
353  'ext' => 'png'
354  )
355  );
356 
363  public $image_effects = array(
364  1 => '-rotate 90',
365  2 => '-rotate 270',
366  3 => '-rotate 180',
367  10 => '-colorspace GRAY',
368  11 => '-sharpen 70',
369  20 => '-normalize',
370  23 => '-contrast',
371  25 => '-gamma 1.3',
372  26 => '-gamma 0.8'
373  );
374 
384  public $data = array();
385 
389  protected $table = '';
390 
396  public $oldData = array();
397 
403  public $alternativeData = '';
404 
410  public $parameters = array();
411 
415  public $currentValKey = 'currentValue_kidjls9dksoje';
416 
423  public $currentRecord = '';
424 
431 
438 
445 
451  public $parentRecord = array();
452 
458  public $INT_include = 0;
459 
465  public $checkPid_cache = array();
466 
470  public $checkPid_badDoktypeList = '255';
471 
477  public $lastTypoLinkUrl = '';
478 
484  public $lastTypoLinkTarget = '';
485 
489  public $lastTypoLinkLD = array();
490 
496  public $substMarkerCache = array();
497 
503  public $recordRegister = array();
504 
510  protected $cObjHookObjectsRegistry = array();
511 
515  public $cObjHookObjectsArr = array();
516 
522  protected $stdWrapHookObjects = array();
523 
530 
534  protected $currentFile = null;
535 
540 
546  protected $userObjectType = false;
547 
551  protected $stopRendering = array();
552 
556  protected $stdWrapRecursionLevel = 0;
557 
562 
566  protected $templateService;
567 
579  const OBJECTTYPE_USER = 2;
580 
585  {
586  $this->typoScriptFrontendController = $typoScriptFrontendController;
587  $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
588  $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
589  }
590 
598  public function __sleep()
599  {
600  $vars = get_object_vars($this);
601  unset($vars['typoScriptFrontendController']);
602  if ($this->currentFile instanceof FileReference) {
603  $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
604  } elseif ($this->currentFile instanceof File) {
605  $this->currentFile = 'File:' . $this->currentFile->getIdentifier();
606  } else {
607  unset($vars['currentFile']);
608  }
609  return array_keys($vars);
610  }
611 
617  public function __wakeup()
618  {
619  if (isset($GLOBALS['TSFE'])) {
620  $this->typoScriptFrontendController = $GLOBALS['TSFE'];
621  }
622  if ($this->currentFile !== null && is_string($this->currentFile)) {
623  list($objectType, $identifier) = explode(':', $this->currentFile, 2);
624  try {
625  if ($objectType === 'File') {
626  $this->currentFile = ResourceFactory::getInstance()->retrieveFileOrFolderObject($identifier);
627  } elseif ($objectType === 'FileReference') {
628  $this->currentFile = ResourceFactory::getInstance()->getFileReferenceObject($identifier);
629  }
630  } catch (ResourceDoesNotExistException $e) {
631  $this->currentFile = null;
632  }
633  }
634  }
635 
646  {
647  $this->contentObjectClassMap = $contentObjectClassMap;
648  }
649 
660  public function registerContentObjectClass($className, $contentObjectName)
661  {
662  $this->contentObjectClassMap[$contentObjectName] = $className;
663  }
664 
674  public function start($data, $table = '')
675  {
676  $this->data = $data;
677  $this->table = $table;
678  $this->currentRecord = $table ? $table . ':' . $this->data['uid'] : '';
679  $this->parameters = array();
680  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'])) {
681  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'] as $classArr) {
682  $this->cObjHookObjectsRegistry[$classArr[0]] = $classArr[1];
683  }
684  }
685  $this->stdWrapHookObjects = array();
686  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'])) {
687  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'] as $classData) {
688  $hookObject = GeneralUtility::getUserObj($classData);
689  if (!$hookObject instanceof ContentObjectStdWrapHookInterface) {
690  throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectStdWrapHookInterface::class, 1195043965);
691  }
692  $this->stdWrapHookObjects[] = $hookObject;
693  }
694  }
695  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'])) {
696  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'] as $classData) {
697  $postInitializationProcessor = GeneralUtility::getUserObj($classData);
698  if (!$postInitializationProcessor instanceof ContentObjectPostInitHookInterface) {
699  throw new \UnexpectedValueException($classData . ' must implement interface ' . ContentObjectPostInitHookInterface::class, 1274563549);
700  }
701  $postInitializationProcessor->postProcessContentObjectInitialization($this);
702  }
703  }
704  }
705 
711  public function getCurrentTable()
712  {
713  return $this->table;
714  }
715 
722  protected function getGetImgResourceHookObjects()
723  {
724  if (!isset($this->getImgResourceHookObjects)) {
725  $this->getImgResourceHookObjects = array();
726  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'])) {
727  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'] as $classData) {
728  $hookObject = GeneralUtility::getUserObj($classData);
729  if (!$hookObject instanceof ContentObjectGetImageResourceHookInterface) {
730  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetImageResourceHookInterface::class, 1218636383);
731  }
732  $this->getImgResourceHookObjects[] = $hookObject;
733  }
734  }
735  }
737  }
738 
748  public function setParent($data, $currentRecord)
749  {
750  $this->parentRecord = array(
751  'data' => $data,
752  'currentRecord' => $currentRecord
753  );
754  }
755 
756  /***********************************************
757  *
758  * CONTENT_OBJ:
759  *
760  ***********************************************/
769  public function getCurrentVal()
770  {
771  return $this->data[$this->currentValKey];
772  }
773 
781  public function setCurrentVal($value)
782  {
783  $this->data[$this->currentValKey] = $value;
784  }
785 
795  public function cObjGet($setup, $addKey = '')
796  {
797  if (!is_array($setup)) {
798  return '';
799  }
800  $sKeyArray = TemplateService::sortedKeyList($setup);
801  $content = '';
802  foreach ($sKeyArray as $theKey) {
803  $theValue = $setup[$theKey];
804  if ((int)$theKey && strpos($theKey, '.') === false) {
805  $conf = $setup[$theKey . '.'];
806  $content .= $this->cObjGetSingle($theValue, $conf, $addKey . $theKey);
807  }
808  }
809  return $content;
810  }
811 
821  public function cObjGetSingle($name, $conf, $TSkey = '__')
822  {
823  $content = '';
824  // Checking that the function is not called eternally. This is done by interrupting at a depth of 100
825  $this->getTypoScriptFrontendController()->cObjectDepthCounter--;
826  if ($this->getTypoScriptFrontendController()->cObjectDepthCounter > 0) {
827  $timeTracker = $this->getTimeTracker();
828  $name = trim($name);
829  if ($timeTracker->LR) {
830  $timeTracker->push($TSkey, $name);
831  }
832  // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
833  if ($name[0] === '<') {
834  $key = trim(substr($name, 1));
835  $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
836  // $name and $conf is loaded with the referenced values.
837  $confOverride = is_array($conf) ? $conf : array();
838  list($name, $conf) = $cF->getVal($key, $this->getTypoScriptFrontendController()->tmpl->setup);
839  $conf = array_replace_recursive(is_array($conf) ? $conf : array(), $confOverride);
840  // Getting the cObject
841  $timeTracker->incStackPointer();
842  $content .= $this->cObjGetSingle($name, $conf, $key);
843  $timeTracker->decStackPointer();
844  } else {
845  $hooked = false;
846  // Application defined cObjects
847  if (!empty($this->cObjHookObjectsRegistry[$name])) {
848  if (empty($this->cObjHookObjectsArr[$name])) {
849  $this->cObjHookObjectsArr[$name] = GeneralUtility::getUserObj($this->cObjHookObjectsRegistry[$name]);
850  }
851  $hookObj = $this->cObjHookObjectsArr[$name];
852  if (method_exists($hookObj, 'cObjGetSingleExt')) {
853  $content .= $hookObj->cObjGetSingleExt($name, $conf, $TSkey, $this);
854  $hooked = true;
855  }
856  }
857  if (!$hooked) {
858  $contentObject = $this->getContentObject($name);
859  if ($contentObject) {
860  $content .= $this->render($contentObject, $conf);
861  } else {
862  // Call hook functions for extra processing
863  if ($name && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'])) {
864  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] as $classData) {
865  $hookObject = GeneralUtility::getUserObj($classData);
866  if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
867  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
868  }
870  $content .= $hookObject->getSingleContentObject($name, (array)$conf, $TSkey, $this);
871  }
872  } else {
873  // Log error in AdminPanel
874  $warning = sprintf('Content Object "%s" does not exist', $name);
875  $timeTracker->setTSlogMessage($warning, 2);
876  }
877  }
878  }
879  }
880  if ($timeTracker->LR) {
881  $timeTracker->pull($content);
882  }
883  }
884  // Increasing on exit...
885  $this->getTypoScriptFrontendController()->cObjectDepthCounter++;
886  return $content;
887  }
888 
898  public function getContentObject($name)
899  {
900  if (!isset($this->contentObjectClassMap[$name])) {
901  return null;
902  }
903  $fullyQualifiedClassName = $this->contentObjectClassMap[$name];
904  $contentObject = GeneralUtility::makeInstance($fullyQualifiedClassName, $this);
905  if (!($contentObject instanceof AbstractContentObject)) {
906  throw new ContentRenderingException(sprintf('Registered content object class name "%" must be an instance of AbstractContentObject, but is not!', $fullyQualifiedClassName), 1422564295);
907  }
908  return $contentObject;
909  }
910 
911  /********************************************
912  *
913  * Functions rendering content objects (cObjects)
914  *
915  ********************************************/
916 
928  public function render(AbstractContentObject $contentObject, $configuration = array())
929  {
930  $content = '';
931 
932  // Evaluate possible cache and return
933  $cacheConfiguration = isset($configuration['cache.']) ? $configuration['cache.'] : null;
934  if ($cacheConfiguration !== null) {
935  unset($configuration['cache.']);
936  $cache = $this->getFromCache($cacheConfiguration);
937  if ($cache !== false) {
938  return $cache;
939  }
940  }
941 
942  // Render content
943  try {
944  $content .= $contentObject->render($configuration);
945  } catch (ContentRenderingException $exception) {
946  // Content rendering Exceptions indicate a critical problem which should not be
947  // caught e.g. when something went wrong with Exception handling itself
948  throw $exception;
949  } catch (\Exception $exception) {
950  $exceptionHandler = $this->createExceptionHandler($configuration);
951  if ($exceptionHandler === null) {
952  throw $exception;
953  } else {
954  $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
955  }
956  }
957 
958  // Store cache
959  if ($cacheConfiguration !== null) {
960  $key = $this->calculateCacheKey($cacheConfiguration);
961  if (!empty($key)) {
963  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
964  $tags = $this->calculateCacheTags($cacheConfiguration);
965  $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
966  $cacheFrontend->set($key, $content, $tags, $lifetime);
967  }
968  }
969 
970  return $content;
971  }
972 
981  protected function createExceptionHandler($configuration = array())
982  {
983  $exceptionHandler = null;
984  $exceptionHandlerClassName = $this->determineExceptionHandlerClassName($configuration);
985  if (!empty($exceptionHandlerClassName)) {
986  $exceptionHandler = GeneralUtility::makeInstance($exceptionHandlerClassName, $this->mergeExceptionHandlerConfiguration($configuration));
987  if (!$exceptionHandler instanceof ExceptionHandlerInterface) {
988  throw new ContentRenderingException('An exception handler was configured but the class does not exist or does not implement the ExceptionHandlerInterface', 1403653369);
989  }
990  }
991 
992  return $exceptionHandler;
993  }
994 
1001  protected function determineExceptionHandlerClassName($configuration)
1002  {
1003  $exceptionHandlerClassName = null;
1004  $tsfe = $this->getTypoScriptFrontendController();
1005  if (!isset($tsfe->config['config']['contentObjectExceptionHandler'])) {
1006  if (GeneralUtility::getApplicationContext()->isProduction()) {
1007  $exceptionHandlerClassName = '1';
1008  }
1009  } else {
1010  $exceptionHandlerClassName = $tsfe->config['config']['contentObjectExceptionHandler'];
1011  }
1012 
1013  if (isset($configuration['exceptionHandler'])) {
1014  $exceptionHandlerClassName = $configuration['exceptionHandler'];
1015  }
1016 
1017  if ($exceptionHandlerClassName === '1') {
1018  $exceptionHandlerClassName = ProductionExceptionHandler::class;
1019  }
1020 
1021  return $exceptionHandlerClassName;
1022  }
1023 
1031  protected function mergeExceptionHandlerConfiguration($configuration)
1032  {
1033  $exceptionHandlerConfiguration = array();
1034  $tsfe = $this->getTypoScriptFrontendController();
1035  if (!empty($tsfe->config['config']['contentObjectExceptionHandler.'])) {
1036  $exceptionHandlerConfiguration = $tsfe->config['config']['contentObjectExceptionHandler.'];
1037  }
1038  if (!empty($configuration['exceptionHandler.'])) {
1039  $exceptionHandlerConfiguration = array_replace_recursive($exceptionHandlerConfiguration, $configuration['exceptionHandler.']);
1040  }
1041 
1042  return $exceptionHandlerConfiguration;
1043  }
1051  public function FLOWPLAYER($conf)
1052  {
1054  return $this->render($this->getContentObject('FLOWPLAYER'), $conf);
1055  }
1056 
1064  public function TEXT($conf)
1065  {
1067  return $this->render($this->getContentObject('TEXT'), $conf);
1068  }
1069 
1077  public function CLEARGIF($conf)
1078  {
1080  return $this->render($this->getContentObject('CLEARGIF'), $conf);
1081  }
1082 
1091  public function COBJ_ARRAY($conf, $ext = '')
1092  {
1094  if ($ext === 'INT') {
1095  return $this->render($this->getContentObject('COA_INT'), $conf);
1096  } else {
1097  return $this->render($this->getContentObject('COA'), $conf);
1098  }
1099  }
1100 
1109  public function USER($conf, $ext = '')
1110  {
1112  if ($ext === 'INT') {
1113  return $this->render($this->getContentObject('USER_INT'), $conf);
1114  } else {
1115  return $this->render($this->getContentObject('USER'), $conf);
1116  }
1117  }
1118 
1127  public function getUserObjectType()
1128  {
1129  return $this->userObjectType;
1130  }
1131 
1139  {
1140  $this->userObjectType = $userObjectType;
1141  }
1142 
1148  public function convertToUserIntObject()
1149  {
1150  if ($this->userObjectType !== self::OBJECTTYPE_USER) {
1151  $this->getTimeTracker()->setTSlogMessage(ContentObjectRenderer::class . '::convertToUserIntObject() is called in the wrong context or for the wrong object type', 2);
1152  } else {
1153  $this->doConvertToUserIntObject = true;
1154  }
1155  }
1156 
1164  public function FILE($conf)
1165  {
1167  return $this->render($this->getContentObject('FILE'), $conf);
1168  }
1169 
1177  public function FILES($conf)
1178  {
1180  return $this->render($this->getContentObject('FILES'), $conf);
1181  }
1182 
1191  public function IMAGE($conf)
1192  {
1194  return $this->render($this->getContentObject('IMAGE'), $conf);
1195  }
1196 
1205  public function IMG_RESOURCE($conf)
1206  {
1208  return $this->render($this->getContentObject('IMG_RESOURCE'), $conf);
1209  }
1210 
1218  public function IMGTEXT($conf)
1219  {
1221  return $this->render($this->getContentObject('IMGTEXT'), $conf);
1222  }
1223 
1231  public function CONTENT($conf)
1232  {
1234  return $this->render($this->getContentObject('CONTENT'), $conf);
1235  }
1236 
1244  public function RECORDS($conf)
1245  {
1247  return $this->render($this->getContentObject('RECORDS'), $conf);
1248  }
1249 
1257  public function HMENU($conf)
1258  {
1260  return $this->render($this->getContentObject('HMENU'), $conf);
1261  }
1262 
1270  public function CTABLE($conf)
1271  {
1273  return $this->render($this->getContentObject('CTABLE'), $conf);
1274  }
1275 
1283  public function OTABLE($conf)
1284  {
1286  return $this->render($this->getContentObject('OTABLE'), $conf);
1287  }
1288 
1296  public function COLUMNS($conf)
1297  {
1299  return $this->render($this->getContentObject('COLUMNS'), $conf);
1300  }
1301 
1309  public function HRULER($conf)
1310  {
1312  return $this->render($this->getContentObject('HRULER'), $conf);
1313  }
1314 
1322  public function CASEFUNC($conf)
1323  {
1325  return $this->render($this->getContentObject('CASE'), $conf);
1326  }
1327 
1337  public function LOAD_REGISTER($conf, $name)
1338  {
1340  if ($name === 'RESTORE_REGISTER') {
1341  return $this->render($this->getContentObject('RESTORE_REGISTER'), $conf);
1342  } else {
1343  return $this->render($this->getContentObject('LOAD_REGISTER'), $conf);
1344  }
1345  }
1346 
1357  public function FORM($conf, $formData = '')
1358  {
1360  return $this->render($this->getContentObject('FORM'), $conf);
1361  }
1362 
1370  public function SEARCHRESULT($conf)
1371  {
1373  return $this->render($this->getContentObject('SEARCHRESULT'), $conf);
1374  }
1375 
1384  public function TEMPLATE($conf)
1385  {
1387  return $this->render($this->getContentObject('TEMPLATE'), $conf);
1388  }
1389 
1397  protected function FLUIDTEMPLATE(array $conf)
1398  {
1400  return $this->render($this->getContentObject('FLUIDTEMPLATE'), $conf);
1401  }
1402 
1410  public function MULTIMEDIA($conf)
1411  {
1413  return $this->render($this->getContentObject('MULTIMEDIA'), $conf);
1414  }
1415 
1423  public function MEDIA($conf)
1424  {
1426  return $this->render($this->getContentObject('MEDIA'), $conf);
1427  }
1428 
1436  public function SWFOBJECT($conf)
1437  {
1439  return $this->render($this->getContentObject('SWFOBJECT'), $conf);
1440  }
1441 
1449  public function QTOBJECT($conf)
1450  {
1452  return $this->render($this->getContentObject('QTOBJECT'), $conf);
1453  }
1454 
1462  public function SVG($conf)
1463  {
1465  return $this->render($this->getContentObject('SVG'), $conf);
1466  }
1467 
1468  /************************************
1469  *
1470  * Various helper functions for content objects:
1471  *
1472  ************************************/
1481  public function readFlexformIntoConf($flexData, &$conf, $recursive = false)
1482  {
1483  if ($recursive === false && is_string($flexData)) {
1484  $flexData = GeneralUtility::xml2array($flexData, 'T3');
1485  }
1486  if (is_array($flexData) && isset($flexData['data']['sDEF']['lDEF'])) {
1487  $flexData = $flexData['data']['sDEF']['lDEF'];
1488  }
1489  if (!is_array($flexData)) {
1490  return;
1491  }
1492  foreach ($flexData as $key => $value) {
1493  if (!is_array($value)) {
1494  continue;
1495  }
1496  if (isset($value['el'])) {
1497  if (is_array($value['el']) && !empty($value['el'])) {
1498  foreach ($value['el'] as $ekey => $element) {
1499  if (isset($element['vDEF'])) {
1500  $conf[$ekey] = $element['vDEF'];
1501  } else {
1502  if (is_array($element)) {
1503  $this->readFlexformIntoConf($element, $conf[$key][key($element)][$ekey], true);
1504  } else {
1505  $this->readFlexformIntoConf($element, $conf[$key][$ekey], true);
1506  }
1507  }
1508  }
1509  } else {
1510  $this->readFlexformIntoConf($value['el'], $conf[$key], true);
1511  }
1512  }
1513  if (isset($value['vDEF'])) {
1514  $conf[$key] = $value['vDEF'];
1515  }
1516  }
1517  }
1518 
1527  public function getSlidePids($pidList, $pidConf)
1528  {
1529  $pidList = isset($pidConf) ? trim($this->stdWrap($pidList, $pidConf)) : trim($pidList);
1530  if ($pidList === '') {
1531  $pidList = 'this';
1532  }
1533  $tsfe = $this->getTypoScriptFrontendController();
1534  $listArr = null;
1535  if (trim($pidList)) {
1536  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $pidList));
1537  $listArr = $this->checkPidArray($listArr);
1538  }
1539  $pidList = array();
1540  if (is_array($listArr) && !empty($listArr)) {
1541  foreach ($listArr as $uid) {
1542  $page = $tsfe->sys_page->getPage($uid);
1543  if (!$page['is_siteroot']) {
1544  $pidList[] = $page['pid'];
1545  }
1546  }
1547  }
1548  return implode(',', $pidList);
1549  }
1550 
1562  public function cImage($file, $conf)
1563  {
1564  $tsfe = $this->getTypoScriptFrontendController();
1565  $info = $this->getImgResource($file, $conf['file.']);
1566  $tsfe->lastImageInfo = $info;
1567  if (!is_array($info)) {
1568  return '';
1569  }
1570  if (is_file(PATH_site . $info['3'])) {
1571  $source = $tsfe->absRefPrefix . GeneralUtility::rawUrlEncodeFP($info['3']);
1572  } else {
1573  $source = $info[3];
1574  }
1575 
1576  $layoutKey = $this->stdWrap($conf['layoutKey'], $conf['layoutKey.']);
1577  $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
1578  $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
1579 
1580  // This array is used to collect the image-refs on the page...
1581  $tsfe->imagesOnPage[] = $source;
1582  $altParam = $this->getAltParam($conf);
1583  $params = $this->stdWrapValue('params', $conf);
1584  if ($params !== '' && $params{0} !== ' ') {
1585  $params = ' ' . $params;
1586  }
1587 
1588  $imageTagValues = array(
1589  'width' => $info[0],
1590  'height' => $info[1],
1591  'src' => htmlspecialchars($source),
1592  'params' => $params,
1593  'altParams' => $altParam,
1594  'border' => $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
1595  'sourceCollection' => $sourceCollection,
1596  'selfClosingTagSlash' => (!empty($tsfe->xhtmlDoctype) ? ' /' : ''),
1597  );
1598 
1599  $theValue = $this->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
1600 
1601  $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
1602  if ($linkWrap) {
1603  $theValue = $this->linkWrap($theValue, $linkWrap);
1604  } elseif ($conf['imageLinkWrap']) {
1605  $originalFile = !empty($info['originalFile']) ? $info['originalFile'] : $info['origFile'];
1606  $theValue = $this->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
1607  }
1608  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
1609  if ($wrap) {
1610  $theValue = $this->wrap($theValue, $conf['wrap']);
1611  }
1612  return $theValue;
1613  }
1614 
1622  public function getBorderAttr($borderAttr)
1623  {
1624  $tsfe = $this->getTypoScriptFrontendController();
1625  if (!GeneralUtility::inList('xhtml_strict,xhtml_11,xhtml_2', $tsfe->xhtmlDoctype) && $tsfe->config['config']['doctype'] != 'html5' && !$tsfe->config['config']['disableImgBorderAttr']) {
1626  return $borderAttr;
1627  }
1628  return '';
1629  }
1630 
1639  public function getImageTagTemplate($layoutKey, $conf)
1640  {
1641  if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
1642  $imageTagLayout = $this->stdWrap($conf['layout.'][$layoutKey . '.']['element'], $conf['layout.'][$layoutKey . '.']['element.']);
1643  } else {
1644  $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1645  }
1646  return $imageTagLayout;
1647  }
1648 
1658  public function getImageSourceCollection($layoutKey, $conf, $file)
1659  {
1660  $sourceCollection = '';
1661  if ($layoutKey && $conf['sourceCollection.'] && ($conf['layout.'][$layoutKey . '.']['source'] || $conf['layout.'][$layoutKey . '.']['source.'])) {
1662 
1663  // find active sourceCollection
1664  $activeSourceCollections = array();
1665  foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
1666  if (substr($sourceCollectionKey, -1) == '.') {
1667  if (empty($sourceCollectionConfiguration['if.']) || $this->checkIf($sourceCollectionConfiguration['if.'])) {
1668  $activeSourceCollections[] = $sourceCollectionConfiguration;
1669  }
1670  }
1671  }
1672 
1673  // apply option split to configurations
1674  $tsfe = $this->getTypoScriptFrontendController();
1675  $srcLayoutOptionSplitted = $tsfe->tmpl->splitConfArray($conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
1676 
1677  // render sources
1678  foreach ($activeSourceCollections as $key => $sourceConfiguration) {
1679  $sourceLayout = $this->stdWrap($srcLayoutOptionSplitted[$key]['source'], $srcLayoutOptionSplitted[$key]['source.']);
1680 
1681  $sourceRenderConfiguration = array(
1682  'file' => $file,
1683  'file.' => $conf['file.']
1684  );
1685 
1686  if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
1687  $imageQuality = isset($sourceConfiguration['quality']) ? $sourceConfiguration['quality'] : '';
1688  if (isset($sourceConfiguration['quality.'])) {
1689  $imageQuality = $this->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
1690  }
1691  if ($imageQuality) {
1692  $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
1693  }
1694  }
1695 
1696  if (isset($sourceConfiguration['pixelDensity'])) {
1697  $pixelDensity = (int)$this->stdWrap($sourceConfiguration['pixelDensity'], $sourceConfiguration['pixelDensity.']);
1698  } else {
1699  $pixelDensity = 1;
1700  }
1701  $dimensionKeys = array('width', 'height', 'maxW', 'minW', 'maxH', 'minH');
1702  foreach ($dimensionKeys as $dimensionKey) {
1703  $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
1704  if (!$dimension) {
1705  $dimension = $this->stdWrap($conf['file.'][$dimensionKey], $conf['file.'][$dimensionKey . '.']);
1706  }
1707  if ($dimension) {
1708  if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
1709  $dimensionParts = explode('c', $dimension, 2);
1710  $dimension = (int)($dimensionParts[0] * $pixelDensity) . 'c';
1711  if ($dimensionParts[1]) {
1712  $dimension .= $dimensionParts[1];
1713  }
1714  } else {
1715  $dimension = (int)($dimension * $pixelDensity);
1716  }
1717  $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
1718  // Remove the stdWrap properties for dimension as they have been processed already above.
1719  unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
1720  }
1721  }
1722  $sourceInfo = $this->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
1723  if ($sourceInfo) {
1724  $sourceConfiguration['width'] = $sourceInfo[0];
1725  $sourceConfiguration['height'] = $sourceInfo[1];
1726  $urlPrefix = '';
1727  if (parse_url($sourceInfo[3], PHP_URL_HOST) === null) {
1728  $urlPrefix = $tsfe->absRefPrefix;
1729  }
1730  $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
1731  $sourceConfiguration['selfClosingTagSlash'] = (!empty($tsfe->xhtmlDoctype) ? ' /' : '');
1732 
1733  $oneSourceCollection = $this->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
1734 
1735  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'])) {
1736  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] as $classData) {
1737  $hookObject = GeneralUtility::getUserObj($classData);
1738  if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
1739  throw new \UnexpectedValueException(
1740  '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
1741  1380007853
1742  );
1743  }
1744  $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this);
1745  }
1746  }
1747 
1748  $sourceCollection .= $oneSourceCollection;
1749  }
1750  }
1751  }
1752  return $sourceCollection;
1753  }
1754 
1764  public function imageLinkWrap($string, $imageFile, $conf)
1765  {
1766  $enable = isset($conf['enable.']) ? $this->stdWrap($conf['enable'], $conf['enable.']) : $conf['enable'];
1767  if (!$enable) {
1768  return $string;
1769  }
1770  $content = $this->typoLink($string, $conf['typolink.']);
1771  if (isset($conf['file.'])) {
1772  $imageFile = $this->stdWrap($imageFile, $conf['file.']);
1773  }
1774 
1775  if ($imageFile instanceof File) {
1776  $file = $imageFile;
1777  } elseif ($imageFile instanceof FileReference) {
1778  $file = $imageFile->getOriginalFile();
1779  } else {
1780  if (MathUtility::canBeInterpretedAsInteger($imageFile)) {
1781  $file = ResourceFactory::getInstance()->getFileObject((int)$imageFile);
1782  } else {
1783  $file = ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($imageFile);
1784  }
1785  }
1786 
1787  // Create imageFileLink if not created with typolink
1788  if ($content == $string) {
1789  $parameterNames = array('width', 'height', 'effects', 'bodyTag', 'title', 'wrap');
1790  $parameters = array();
1791  $sample = isset($conf['sample.']) ? $this->stdWrap($conf['sample'], $conf['sample.']) : $conf['sample'];
1792  if ($sample) {
1793  $parameters['sample'] = 1;
1794  }
1795  foreach ($parameterNames as $parameterName) {
1796  if (isset($conf[$parameterName . '.'])) {
1797  $conf[$parameterName] = $this->stdWrap($conf[$parameterName], $conf[$parameterName . '.']);
1798  }
1799  if (isset($conf[$parameterName]) && $conf[$parameterName]) {
1800  $parameters[$parameterName] = $conf[$parameterName];
1801  }
1802  }
1803  $parametersEncoded = base64_encode(serialize($parameters));
1804  $hmac = GeneralUtility::hmac(implode('|', array($file->getUid(), $parametersEncoded)));
1805  $params = '&md5=' . $hmac;
1806  foreach (str_split($parametersEncoded, 64) as $index => $chunk) {
1807  $params .= '&parameters' . rawurlencode('[') . $index . rawurlencode(']') . '=' . rawurlencode($chunk);
1808  }
1809  $url = $this->getTypoScriptFrontendController()->absRefPrefix . 'index.php?eID=tx_cms_showpic&file=' . $file->getUid() . $params;
1810  $directImageLink = isset($conf['directImageLink.']) ? $this->stdWrap($conf['directImageLink'], $conf['directImageLink.']) : $conf['directImageLink'];
1811  if ($directImageLink) {
1812  $imgResourceConf = array(
1813  'file' => $imageFile,
1814  'file.' => $conf
1815  );
1816  $url = $this->cObjGetSingle('IMG_RESOURCE', $imgResourceConf);
1817  if (!$url) {
1818  // If no imagemagick / gm is available
1819  $url = $imageFile;
1820  }
1821  }
1822  // Create TARGET-attribute only if the right doctype is used
1823  if (!GeneralUtility::inList('xhtml_strict,xhtml_11,xhtml_2', $this->getTypoScriptFrontendController()->xhtmlDoctype)) {
1824  $target = isset($conf['target.']) ? $this->stdWrap($conf['target'], $conf['target.']) : $conf['target'];
1825  if ($target) {
1826  $target = sprintf(' target="%s"', $target);
1827  } else {
1828  $target = ' target="thePicture"';
1829  }
1830  } else {
1831  $target = '';
1832  }
1833  $a1 = '';
1834  $a2 = '';
1835  $conf['JSwindow'] = isset($conf['JSwindow.']) ? $this->stdWrap($conf['JSwindow'], $conf['JSwindow.']) : $conf['JSwindow'];
1836  if ($conf['JSwindow']) {
1837  if ($conf['JSwindow.']['altUrl'] || $conf['JSwindow.']['altUrl.']) {
1838  $altUrl = isset($conf['JSwindow.']['altUrl.']) ? $this->stdWrap($conf['JSwindow.']['altUrl'], $conf['JSwindow.']['altUrl.']) : $conf['JSwindow.']['altUrl'];
1839  if ($altUrl) {
1840  $url = $altUrl . ($conf['JSwindow.']['altUrl_noDefaultParams'] ? '' : '?file=' . rawurlencode($imageFile) . $params);
1841  }
1842  }
1843 
1844  $processedFile = $file->process('Image.CropScaleMask', $conf);
1845  $JSwindowExpand = isset($conf['JSwindow.']['expand.']) ? $this->stdWrap($conf['JSwindow.']['expand'], $conf['JSwindow.']['expand.']) : $conf['JSwindow.']['expand'];
1846  $offset = GeneralUtility::intExplode(',', $JSwindowExpand . ',');
1847  $newWindow = isset($conf['JSwindow.']['newWindow.']) ? $this->stdWrap($conf['JSwindow.']['newWindow'], $conf['JSwindow.']['newWindow.']) : $conf['JSwindow.']['newWindow'];
1848  $onClick = 'openPic('
1849  . GeneralUtility::quoteJSvalue($this->getTypoScriptFrontendController()->baseUrlWrap($url)) . ','
1850  . '\'' . ($newWindow ? md5($url) : 'thePicture') . '\','
1851  . GeneralUtility::quoteJSvalue('width=' . ($processedFile->getProperty('width') + $offset[0])
1852  . ',height=' . ($processedFile->getProperty('height') + $offset[1]) . ',status=0,menubar=0')
1853  . '); return false;';
1854  $a1 = '<a href="' . htmlspecialchars($url) . '" onclick="' . htmlspecialchars($onClick) . '"' . $target . $this->getTypoScriptFrontendController()->ATagParams . '>';
1855  $a2 = '</a>';
1856  $this->getTypoScriptFrontendController()->setJS('openPic');
1857  } else {
1858  $conf['linkParams.']['parameter'] = $url;
1859  $string = $this->typoLink($string, $conf['linkParams.']);
1860  }
1861  if (isset($conf['stdWrap.'])) {
1862  $string = $this->stdWrap($string, $conf['stdWrap.']);
1863  }
1864  $content = $a1 . $string . $a2;
1865  }
1866  return $content;
1867  }
1868 
1877  public function fileResource($fName, $addParams = 'alt="" title=""')
1878  {
1879  $tsfe = $this->getTypoScriptFrontendController();
1880  $incFile = $tsfe->tmpl->getFileName($fName);
1881  if ($incFile && file_exists($incFile)) {
1882  $fileinfo = GeneralUtility::split_fileref($incFile);
1883  if (GeneralUtility::inList('jpg,gif,jpeg,png', $fileinfo['fileext'])) {
1884  $imgFile = $incFile;
1885  $imgInfo = @getImageSize($imgFile);
1886  return '<img src="' . $tsfe->absRefPrefix . $imgFile . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '"' . $this->getBorderAttr(' border="0"') . ' ' . $addParams . ' />';
1887  } elseif (filesize($incFile) < 1024 * 1024) {
1888  return $tsfe->tmpl->fileContent($incFile);
1889  }
1890  }
1891  return '';
1892  }
1893 
1903  public function lastChanged($tstamp)
1904  {
1905  $tstamp = (int)$tstamp;
1906  $tsfe = $this->getTypoScriptFrontendController();
1907  if ($tstamp > (int)$tsfe->register['SYS_LASTCHANGED']) {
1908  $tsfe->register['SYS_LASTCHANGED'] = $tstamp;
1909  }
1910  }
1911 
1921  public function linkWrap($content, $wrap)
1922  {
1923  $wrapArr = explode('|', $wrap);
1924  if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
1925  if ($uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid']) {
1926  $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
1927  }
1928  }
1929  return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
1930  }
1931 
1941  public function getAltParam($conf, $longDesc = true)
1942  {
1943  $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
1944  $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
1945  if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] != 'html5') {
1946  $longDescUrl = $this->typoLink_URL($conf['longdescURL.']);
1947  } else {
1948  $longDescUrl = trim($conf['longdescURL']);
1949  }
1950  $longDescUrl = strip_tags($longDescUrl);
1951 
1952  // "alt":
1953  $altParam = ' alt="' . htmlspecialchars($altText) . '"';
1954  // "title":
1955  $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
1956  // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
1957  if ($titleText || $emptyTitleHandling == 'keepEmpty') {
1958  $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
1959  } elseif (!$titleText && $emptyTitleHandling == 'useAlt') {
1960  $altParam .= ' title="' . htmlspecialchars($altText) . '"';
1961  }
1962  // "longDesc" URL
1963  if ($longDesc && !empty($longDescUrl)) {
1964  $altParam .= ' longdesc="' . htmlspecialchars($longDescUrl) . '"';
1965  }
1966  return $altParam;
1967  }
1968 
1977  public function cleanFormName($name)
1978  {
1979  GeneralUtility::logDeprecatedFunction();
1980  // Turn data[x][y] into data:x:y:
1981  $name = preg_replace('/\\[|\\]\\[?/', ':', trim($name));
1982  // Remove illegal chars like _
1983  return preg_replace('#[^:a-zA-Z0-9]#', '', $name);
1984  }
1985 
1995  public function getATagParams($conf, $addGlobal = 1)
1996  {
1997  $aTagParams = '';
1998  if ($conf['ATagParams.']) {
1999  $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
2000  } elseif ($conf['ATagParams']) {
2001  $aTagParams = ' ' . $conf['ATagParams'];
2002  }
2003  if ($addGlobal) {
2004  $aTagParams = ' ' . trim($this->getTypoScriptFrontendController()->ATagParams . $aTagParams);
2005  }
2006  // Extend params
2007  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'])) {
2008  $_params = array(
2009  'conf' => &$conf,
2010  'aTagParams' => &$aTagParams
2011  );
2012  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] as $objRef) {
2013  $processor =& GeneralUtility::getUserObj($objRef);
2014  $aTagParams = $processor->process($_params, $this);
2015  }
2016  }
2017 
2018  $aTagParams = trim($aTagParams);
2019  if (!empty($aTagParams)) {
2020  $aTagParams = ' ' . $aTagParams;
2021  }
2022 
2023  return $aTagParams;
2024  }
2025 
2034  public function extLinkATagParams($URL, $TYPE)
2035  {
2036  $out = '';
2037  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
2038  $extLinkATagParamsHandler = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
2039  if (method_exists($extLinkATagParamsHandler, 'main')) {
2040  $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
2041  }
2042  }
2043  return trim($out) ? ' ' . trim($out) : '';
2044  }
2045 
2046  /***********************************************
2047  *
2048  * HTML template processing functions
2049  *
2050  ***********************************************/
2069  public function getSubpart($content, $marker)
2070  {
2071  return $this->templateService->getSubpart($content, $marker);
2072  }
2073 
2086  public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1)
2087  {
2088  return $this->templateService->substituteSubpart($content, $marker, $subpartContent, $recursive);
2089  }
2090 
2098  public function substituteSubpartArray($content, array $subpartsContent)
2099  {
2100  return $this->templateService->substituteSubpartArray($content, $subpartsContent);
2101  }
2102 
2113  public function substituteMarker($content, $marker, $markContent)
2114  {
2115  return $this->templateService->substituteMarker($content, $marker, $markContent);
2116  }
2117 
2150  public function substituteMarkerArrayCached($content, array $markContentArray = null, array $subpartContentArray = null, array $wrappedSubpartContentArray = null)
2151  {
2152  $timeTracker = $this->getTimeTracker();
2153  $timeTracker->push('substituteMarkerArrayCached');
2154  // If not arrays then set them
2155  if (is_null($markContentArray)) {
2156  // Plain markers
2157  $markContentArray = array();
2158  }
2159  if (is_null($subpartContentArray)) {
2160  // Subparts being directly substituted
2161  $subpartContentArray = array();
2162  }
2163  if (is_null($wrappedSubpartContentArray)) {
2164  // Subparts being wrapped
2165  $wrappedSubpartContentArray = array();
2166  }
2167  // Finding keys and check hash:
2168  $sPkeys = array_keys($subpartContentArray);
2169  $wPkeys = array_keys($wrappedSubpartContentArray);
2170  $aKeys = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
2171  if (empty($aKeys)) {
2172  $timeTracker->pull();
2173  return $content;
2174  }
2175  asort($aKeys);
2176  $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize(array(
2177  $content,
2178  $aKeys
2179  )));
2180  if ($this->substMarkerCache[$storeKey]) {
2181  $storeArr = $this->substMarkerCache[$storeKey];
2182  $timeTracker->setTSlogMessage('Cached', 0);
2183  } else {
2184  $storeArrDat = $this->getTypoScriptFrontendController()->sys_page->getHash($storeKey);
2185  if (is_array($storeArrDat)) {
2186  $storeArr = $storeArrDat;
2187  // Setting cache:
2188  $this->substMarkerCache[$storeKey] = $storeArr;
2189  $timeTracker->setTSlogMessage('Cached from DB', 0);
2190  } else {
2191  // Initialize storeArr
2192  $storeArr = array();
2193  // Finding subparts and substituting them with the subpart as a marker
2194  foreach ($sPkeys as $sPK) {
2195  $content = $this->substituteSubpart($content, $sPK, $sPK);
2196  }
2197  // Finding subparts and wrapping them with markers
2198  foreach ($wPkeys as $wPK) {
2199  $content = $this->substituteSubpart($content, $wPK, array(
2200  $wPK,
2201  $wPK
2202  ));
2203  }
2204  // Traverse keys and quote them for reg ex.
2205  foreach ($aKeys as $tK => $tV) {
2206  $aKeys[$tK] = preg_quote($tV, '/');
2207  }
2208  $regex = '/' . implode('|', $aKeys) . '/';
2209  // Doing regex's
2210  $storeArr['c'] = preg_split($regex, $content);
2211  preg_match_all($regex, $content, $keyList);
2212  $storeArr['k'] = $keyList[0];
2213  // Setting cache:
2214  $this->substMarkerCache[$storeKey] = $storeArr;
2215  // Storing the cached data:
2216  $this->getTypoScriptFrontendController()->sys_page->storeHash($storeKey, $storeArr, 'substMarkArrayCached');
2217  $timeTracker->setTSlogMessage('Parsing', 0);
2218  }
2219  }
2220  // Substitution/Merging:
2221  // Merging content types together, resetting
2222  $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
2223  $wSCA_reg = array();
2224  $content = '';
2225  // Traversing the keyList array and merging the static and dynamic content
2226  foreach ($storeArr['k'] as $n => $keyN) {
2227  $content .= $storeArr['c'][$n];
2228  if (!is_array($valueArr[$keyN])) {
2229  $content .= $valueArr[$keyN];
2230  } else {
2231  $content .= $valueArr[$keyN][(int)$wSCA_reg[$keyN] % 2];
2232  $wSCA_reg[$keyN]++;
2233  }
2234  }
2235  $content .= $storeArr['c'][count($storeArr['k'])];
2236  $timeTracker->pull();
2237  return $content;
2238  }
2239 
2259  public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = false, $deleteUnused = false)
2260  {
2261  return $this->templateService->substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
2262  }
2263 
2272  public function substituteMarkerInObject(&$tree, array $markContentArray)
2273  {
2274  if (is_array($tree)) {
2275  foreach ($tree as $key => $value) {
2276  $this->substituteMarkerInObject($tree[$key], $markContentArray);
2277  }
2278  } else {
2279  $tree = $this->substituteMarkerArray($tree, $markContentArray);
2280  }
2281  return $tree;
2282  }
2283 
2294  public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = false, $deleteUnused = false)
2295  {
2296  return $this->templateService->substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
2297  }
2298 
2311  public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = true, $prefix = 'FIELD_', $HSC = false)
2312  {
2313  $tsfe = $this->getTypoScriptFrontendController();
2314  if ($fieldList) {
2315  $fArr = GeneralUtility::trimExplode(',', $fieldList, true);
2316  foreach ($fArr as $field) {
2317  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], !empty($tsfe->xhtmlDoctype)) : $row[$field];
2318  }
2319  } else {
2320  if (is_array($row)) {
2321  foreach ($row as $field => $value) {
2323  if ($HSC) {
2324  $value = htmlspecialchars($value);
2325  }
2326  $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, !empty($tsfe->xhtmlDoctype)) : $value;
2327  }
2328  }
2329  }
2330  }
2331  return $markContentArray;
2332  }
2333 
2339  public function setCurrentFile($fileObject)
2340  {
2341  $this->currentFile = $fileObject;
2342  }
2343 
2349  public function getCurrentFile()
2350  {
2351  return $this->currentFile;
2352  }
2353 
2354  /***********************************************
2355  *
2356  * "stdWrap" + sub functions
2357  *
2358  ***********************************************/
2370  public function stdWrap($content = '', $conf = array())
2371  {
2372  // If there is any hook object, activate all of the process and override functions.
2373  // The hook interface ContentObjectStdWrapHookInterface takes care that all 4 methods exist.
2374  if ($this->stdWrapHookObjects) {
2375  $conf['stdWrapPreProcess'] = 1;
2376  $conf['stdWrapOverride'] = 1;
2377  $conf['stdWrapProcess'] = 1;
2378  $conf['stdWrapPostProcess'] = 1;
2379  }
2380 
2381  if (!is_array($conf) || !$conf) {
2382  return $content;
2383  }
2384 
2385  // Cache handling
2386  if (is_array($conf['cache.'])) {
2387  $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
2388  $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
2389  $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
2390  $conf['cacheRead'] = 1;
2391  $conf['cacheStore'] = 1;
2392  }
2393  // Check, which of the available stdWrap functions is needed for the current conf Array
2394  // and keep only those but still in the same order
2395  $sortedConf = array_intersect_key($this->stdWrapOrder, $conf);
2396  // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
2397  $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
2398  // Additional Array to check whether a function has already been executed
2399  $isExecuted = array();
2400  // Additional switch to make sure 'required', 'if' and 'fieldRequired'
2401  // will still stop rendering immediately in case they return FALSE
2402  $this->stdWrapRecursionLevel++;
2403  $this->stopRendering[$this->stdWrapRecursionLevel] = false;
2404  // execute each function in the predefined order
2405  foreach ($sortedConf as $stdWrapName => $functionType) {
2406  // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
2407  if (!$isExecuted[$stdWrapName] && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
2408  $functionName = rtrim($stdWrapName, '.');
2409  $functionProperties = $functionName . '.';
2410  // If there is any code on the next level, check if it contains "official" stdWrap functions
2411  // if yes, execute them first - will make each function stdWrap aware
2412  // so additional stdWrap calls within the functions can be removed, since the result will be the same
2413  // exception: the recursive stdWrap function and cObject will still be using their own stdWrap call, since it modifies the content and not a property
2414  if ($functionName !== 'stdWrap' && !empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
2415  if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
2416  $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
2417  }
2418  }
2419  // Check if key is still containing something, since it might have been changed by next level stdWrap before
2420  if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
2421  // Get just that part of $conf that is needed for the particular function
2422  $singleConf = array(
2423  $functionName => $conf[$functionName],
2424  $functionProperties => $conf[$functionProperties]
2425  );
2426  // In this special case 'spaceBefore' and 'spaceAfter' need additional stuff from 'space.''
2427  if ($functionName === 'spaceBefore' || $functionName === 'spaceAfter') {
2428  $singleConf['space.'] = $conf['space.'];
2429  }
2430  // Hand over the whole $conf array to the stdWrapHookObjects
2431  if ($functionType === 'hook') {
2432  $singleConf = $conf;
2433  }
2434  // Add both keys - with and without the dot - to the set of executed functions
2435  $isExecuted[$functionName] = true;
2436  $isExecuted[$functionProperties] = true;
2437  // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
2438  $functionName = 'stdWrap_' . $functionName;
2439  $content = $this->{$functionName}($content, $singleConf);
2440  } elseif ($functionType == 'boolean' && !$conf[$functionName]) {
2441  $isExecuted[$functionName] = true;
2442  $isExecuted[$functionProperties] = true;
2443  }
2444  }
2445  }
2446  unset($this->stopRendering[$this->stdWrapRecursionLevel]);
2447  $this->stdWrapRecursionLevel--;
2448 
2449  return $content;
2450  }
2451 
2460  public function stdWrapValue($key, array $config, $defaultValue = '')
2461  {
2462  if (isset($config[$key])) {
2463  if (!isset($config[$key . '.'])) {
2464  return $config[$key];
2465  }
2466  } elseif (isset($config[$key . '.'])) {
2467  $config[$key] = '';
2468  } else {
2469  return $defaultValue;
2470  }
2471  $stdWrapped = $this->stdWrap($config[$key], $config[$key . '.']);
2472  return $stdWrapped ?: $defaultValue;
2473  }
2474 
2484  public function stdWrap_stdWrapPreProcess($content = '', $conf = array())
2485  {
2486  foreach ($this->stdWrapHookObjects as $hookObject) {
2488  $content = $hookObject->stdWrapPreProcess($content, $conf, $this);
2489  }
2490  return $content;
2491  }
2492 
2500  public function stdWrap_cacheRead($content = '', $conf = array())
2501  {
2502  if (!isset($conf['cache.'])) {
2503  return $content;
2504  }
2505  $result = $this->getFromCache($conf['cache.']);
2506  return $result === false ? $content : $result;
2507  }
2508 
2516  public function stdWrap_addPageCacheTags($content = '', $conf = array())
2517  {
2518  $tags = isset($conf['addPageCacheTags.'])
2519  ? $this->stdWrap($conf['addPageCacheTags'], $conf['addPageCacheTags.'])
2520  : $conf['addPageCacheTags'];
2521  if (!empty($tags)) {
2522  $cacheTags = GeneralUtility::trimExplode(',', $tags, true);
2523  $this->getTypoScriptFrontendController()->addCacheTags($cacheTags);
2524  }
2525  return $content;
2526  }
2527 
2535  public function stdWrap_setContentToCurrent($content = '')
2536  {
2537  $this->data[$this->currentValKey] = $content;
2538  return $content;
2539  }
2540 
2549  public function stdWrap_setCurrent($content = '', $conf = array())
2550  {
2551  $this->data[$this->currentValKey] = $conf['setCurrent'];
2552  return $content;
2553  }
2554 
2563  public function stdWrap_lang($content = '', $conf = array())
2564  {
2565  $tsfe = $this->getTypoScriptFrontendController();
2566  if (isset($conf['lang.']) && $tsfe->config['config']['language'] && isset($conf['lang.'][$tsfe->config['config']['language']])) {
2567  $content = $conf['lang.'][$tsfe->config['config']['language']];
2568  }
2569  return $content;
2570  }
2571 
2580  public function stdWrap_data($content = '', $conf = array())
2581  {
2582  $content = $this->getData($conf['data'], is_array($this->alternativeData) ? $this->alternativeData : $this->data);
2583  // This must be unset directly after
2584  $this->alternativeData = '';
2585  return $content;
2586  }
2587 
2596  public function stdWrap_field($content = '', $conf = array())
2597  {
2598  return $this->getFieldVal($conf['field']);
2599  }
2600 
2610  public function stdWrap_current($content = '', $conf = array())
2611  {
2612  return $this->data[$this->currentValKey];
2613  }
2614 
2624  public function stdWrap_cObject($content = '', $conf = array())
2625  {
2626  return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
2627  }
2628 
2638  public function stdWrap_numRows($content = '', $conf = array())
2639  {
2640  return $this->numRows($conf['numRows.']);
2641  }
2642 
2651  public function stdWrap_filelist($content = '', $conf = array())
2652  {
2653  return $this->filelist($conf['filelist']);
2654  }
2655 
2664  public function stdWrap_preUserFunc($content = '', $conf = array())
2665  {
2666  return $this->callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'], $content);
2667  }
2668 
2678  public function stdWrap_stdWrapOverride($content = '', $conf = array())
2679  {
2680  foreach ($this->stdWrapHookObjects as $hookObject) {
2682  $content = $hookObject->stdWrapOverride($content, $conf, $this);
2683  }
2684  return $content;
2685  }
2686 
2695  public function stdWrap_override($content = '', $conf = array())
2696  {
2697  if (trim($conf['override'])) {
2698  $content = $conf['override'];
2699  }
2700  return $content;
2701  }
2702 
2712  public function stdWrap_preIfEmptyListNum($content = '', $conf = array())
2713  {
2714  return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
2715  }
2716 
2725  public function stdWrap_ifNull($content = '', $conf = array())
2726  {
2727  return $content !== null ? $content : $conf['ifNull'];
2728  }
2729 
2739  public function stdWrap_ifEmpty($content = '', $conf = array())
2740  {
2741  if (!trim($content)) {
2742  $content = $conf['ifEmpty'];
2743  }
2744  return $content;
2745  }
2746 
2756  public function stdWrap_ifBlank($content = '', $conf = array())
2757  {
2758  if (trim($content) === '') {
2759  $content = $conf['ifBlank'];
2760  }
2761  return $content;
2762  }
2763 
2774  public function stdWrap_listNum($content = '', $conf = array())
2775  {
2776  return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
2777  }
2778 
2786  public function stdWrap_trim($content = '')
2787  {
2788  return trim($content);
2789  }
2790 
2799  public function stdWrap_strPad($content = '', $conf = array())
2800  {
2801  // Must specify a length in conf for this to make sense
2802  $length = 0;
2803  // Padding with space is PHP-default
2804  $padWith = ' ';
2805  // Padding on the right side is PHP-default
2806  $padType = STR_PAD_RIGHT;
2807  if (!empty($conf['strPad.']['length'])) {
2808  $length = isset($conf['strPad.']['length.']) ? $this->stdWrap($conf['strPad.']['length'], $conf['strPad.']['length.']) : $conf['strPad.']['length'];
2809  $length = (int)$length;
2810  }
2811  if (isset($conf['strPad.']['padWith']) && (string)$conf['strPad.']['padWith'] !== '') {
2812  $padWith = isset($conf['strPad.']['padWith.']) ? $this->stdWrap($conf['strPad.']['padWith'], $conf['strPad.']['padWith.']) : $conf['strPad.']['padWith'];
2813  }
2814  if (!empty($conf['strPad.']['type'])) {
2815  $type = isset($conf['strPad.']['type.']) ? $this->stdWrap($conf['strPad.']['type'], $conf['strPad.']['type.']) : $conf['strPad.']['type'];
2816  if (strtolower($type) === 'left') {
2817  $padType = STR_PAD_LEFT;
2818  } elseif (strtolower($type) === 'both') {
2819  $padType = STR_PAD_BOTH;
2820  }
2821  }
2822  return str_pad($content, $length, $padWith, $padType);
2823  }
2824 
2836  public function stdWrap_stdWrap($content = '', $conf = array())
2837  {
2838  return $this->stdWrap($content, $conf['stdWrap.']);
2839  }
2840 
2850  public function stdWrap_stdWrapProcess($content = '', $conf = array())
2851  {
2852  foreach ($this->stdWrapHookObjects as $hookObject) {
2854  $content = $hookObject->stdWrapProcess($content, $conf, $this);
2855  }
2856  return $content;
2857  }
2858 
2867  public function stdWrap_required($content = '')
2868  {
2869  if ((string)$content === '') {
2870  $content = '';
2871  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2872  }
2873  return $content;
2874  }
2875 
2885  public function stdWrap_if($content = '', $conf = array())
2886  {
2887  if (empty($conf['if.']) || $this->checkIf($conf['if.'])) {
2888  return $content;
2889  }
2890  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2891  return '';
2892  }
2893 
2903  public function stdWrap_fieldRequired($content = '', $conf = array())
2904  {
2905  if (!trim($this->data[$conf['fieldRequired']])) {
2906  $content = '';
2907  $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2908  }
2909  return $content;
2910  }
2911 
2920  public function stdWrap_csConv($content = '', $conf = array())
2921  {
2922  return $this->getTypoScriptFrontendController()->csConv($content, $conf['csConv']);
2923  }
2924 
2934  public function stdWrap_parseFunc($content = '', $conf = array())
2935  {
2936  return $this->parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
2937  }
2938 
2948  public function stdWrap_HTMLparser($content = '', $conf = array())
2949  {
2950  if (is_array($conf['HTMLparser.'])) {
2951  $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
2952  }
2953  return $content;
2954  }
2955 
2965  public function stdWrap_split($content = '', $conf = array())
2966  {
2967  return $this->splitObj($content, $conf['split.']);
2968  }
2969 
2978  public function stdWrap_replacement($content = '', $conf = array())
2979  {
2980  return $this->replacement($content, $conf['replacement.']);
2981  }
2982 
2992  public function stdWrap_prioriCalc($content = '', $conf = array())
2993  {
2994  $content = MathUtility::calculateWithParentheses($content);
2995  if ($conf['prioriCalc'] == 'intval') {
2996  $content = (int)$content;
2997  }
2998  return $content;
2999  }
3000 
3009  public function stdWrap_char($content = '', $conf = array())
3010  {
3011  return chr((int)$conf['char']);
3012  }
3013 
3021  public function stdWrap_intval($content = '')
3022  {
3023  return (int)$content;
3024  }
3025 
3034  public function stdWrap_hash($content = '', array $conf = array())
3035  {
3036  $algorithm = isset($conf['hash.']) ? $this->stdWrap($conf['hash'], $conf['hash.']) : $conf['hash'];
3037  if (function_exists('hash') && in_array($algorithm, hash_algos())) {
3038  return hash($algorithm, $content);
3039  }
3040  // Non-existing hashing algorithm
3041  return '';
3042  }
3043 
3052  public function stdWrap_round($content = '', $conf = array())
3053  {
3054  return $this->round($content, $conf['round.']);
3055  }
3056 
3065  public function stdWrap_numberFormat($content = '', $conf = array())
3066  {
3067  return $this->numberFormat($content, $conf['numberFormat.']);
3068  }
3069 
3077  public function stdWrap_expandList($content = '')
3078  {
3079  return GeneralUtility::expandList($content);
3080  }
3081 
3091  public function stdWrap_date($content = '', $conf = array())
3092  {
3093  // Check for zero length string to mimic default case of date/gmdate.
3094  $content = $content == '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
3095  $content = $conf['date.']['GMT'] ? gmdate($conf['date'], $content) : date($conf['date'], $content);
3096  return $content;
3097  }
3098 
3108  public function stdWrap_strftime($content = '', $conf = array())
3109  {
3110  // Check for zero length string to mimic default case of strtime/gmstrftime
3111  $content = $content == '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
3112  $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
3113  $tsfe = $this->getTypoScriptFrontendController();
3114  $tmp_charset = $conf['strftime.']['charset'] ? $conf['strftime.']['charset'] : $tsfe->localeCharset;
3115  if ($tmp_charset) {
3116  $content = $tsfe->csConv($content, $tmp_charset);
3117  }
3118  return $content;
3119  }
3120 
3129  public function stdWrap_strtotime($content = '', $conf = array())
3130  {
3131  if ($conf['strtotime'] !== '1') {
3132  $content .= ' ' . $conf['strtotime'];
3133  }
3134  return strtotime($content, $GLOBALS['EXEC_TIME']);
3135  }
3136 
3145  public function stdWrap_age($content = '', $conf = array())
3146  {
3147  return $this->calcAge($GLOBALS['EXEC_TIME'] - $content, $conf['age']);
3148  }
3149 
3159  public function stdWrap_case($content = '', $conf = array())
3160  {
3161  return $this->HTMLcaseshift($content, $conf['case']);
3162  }
3163 
3172  public function stdWrap_bytes($content = '', $conf = array())
3173  {
3174  return GeneralUtility::formatSize($content, $conf['bytes.']['labels'], $conf['bytes.']['base']);
3175  }
3176 
3185  public function stdWrap_substring($content = '', $conf = array())
3186  {
3187  return $this->substring($content, $conf['substring']);
3188  }
3189 
3198  public function stdWrap_removeBadHTML($content = '')
3199  {
3200  return $this->removeBadHTML($content);
3201  }
3202 
3211  public function stdWrap_cropHTML($content = '', $conf = array())
3212  {
3213  return $this->cropHTML($content, $conf['cropHTML']);
3214  }
3215 
3223  public function stdWrap_stripHtml($content = '')
3224  {
3225  return strip_tags($content);
3226  }
3227 
3236  public function stdWrap_crop($content = '', $conf = array())
3237  {
3238  return $this->crop($content, $conf['crop']);
3239  }
3240 
3248  public function stdWrap_rawUrlEncode($content = '')
3249  {
3250  return rawurlencode($content);
3251  }
3252 
3262  public function stdWrap_htmlSpecialChars($content = '', $conf = array())
3263  {
3264  if (!empty($conf['htmlSpecialChars.']['preserveEntities'])) {
3265  $content = htmlspecialchars($content, ENT_COMPAT, 'UTF-8', false);
3266  } else {
3267  $content = htmlspecialchars($content);
3268  }
3269  return $content;
3270  }
3271 
3280  public function stdWrap_encodeForJavaScriptValue($content = '')
3281  {
3282  return GeneralUtility::quoteJSvalue($content);
3283  }
3284 
3293  public function stdWrap_doubleBrTag($content = '', $conf = array())
3294  {
3295  return preg_replace('/
3296 ?
3297 [ ]*
3298 ?
3299 /', $conf['doubleBrTag'], $content);
3300  }
3301 
3311  public function stdWrap_br($content = '')
3312  {
3313  return nl2br($content, !empty($this->getTypoScriptFrontendController()->xhtmlDoctype));
3314  }
3315 
3324  public function stdWrap_brTag($content = '', $conf = array())
3325  {
3326  return str_replace(LF, $conf['brTag'], $content);
3327  }
3328 
3338  public function stdWrap_encapsLines($content = '', $conf = array())
3339  {
3340  return $this->encaps_lineSplit($content, $conf['encapsLines.']);
3341  }
3342 
3350  public function stdWrap_keywords($content = '')
3351  {
3352  return $this->keywords($content);
3353  }
3354 
3364  public function stdWrap_innerWrap($content = '', $conf = array())
3365  {
3366  return $this->wrap($content, $conf['innerWrap']);
3367  }
3368 
3378  public function stdWrap_innerWrap2($content = '', $conf = array())
3379  {
3380  return $this->wrap($content, $conf['innerWrap2']);
3381  }
3382 
3393  public function stdWrap_fontTag($content = '', $conf = array())
3394  {
3395  return $this->wrap($content, $conf['fontTag']);
3396  }
3397 
3406  public function stdWrap_addParams($content = '', $conf = array())
3407  {
3408  return $this->addParams($content, $conf['addParams.']);
3409  }
3410 
3420  public function stdWrap_textStyle($content = '', $conf = array())
3421  {
3422  return $this->textStyle($content, $conf['textStyle.']);
3423  }
3424 
3434  public function stdWrap_tableStyle($content = '', $conf = array())
3435  {
3436  return $this->tableStyle($content, $conf['tableStyle.']);
3437  }
3438 
3448  public function stdWrap_filelink($content = '', $conf = array())
3449  {
3450  return $this->filelink($content, $conf['filelink.']);
3451  }
3452 
3461  public function stdWrap_preCObject($content = '', $conf = array())
3462  {
3463  return $this->cObjGetSingle($conf['preCObject'], $conf['preCObject.'], '/stdWrap/.preCObject') . $content;
3464  }
3465 
3474  public function stdWrap_postCObject($content = '', $conf = array())
3475  {
3476  return $content . $this->cObjGetSingle($conf['postCObject'], $conf['postCObject.'], '/stdWrap/.postCObject');
3477  }
3478 
3488  public function stdWrap_wrapAlign($content = '', $conf = array())
3489  {
3490  $wrapAlign = trim($conf['wrapAlign']);
3491  if ($wrapAlign) {
3492  $content = $this->wrap($content, '<div style="text-align:' . $wrapAlign . ';">|</div>');
3493  }
3494  return $content;
3495  }
3496 
3507  public function stdWrap_typolink($content = '', $conf = array())
3508  {
3509  return $this->typoLink($content, $conf['typolink.']);
3510  }
3511 
3520  public function stdWrap_TCAselectItem($content = '', $conf = array())
3521  {
3522  if (is_array($conf['TCAselectItem.'])) {
3523  $content = $this->TCAlookup($content, $conf['TCAselectItem.']);
3524  }
3525  return $content;
3526  }
3527 
3537  public function stdWrap_spaceBefore($content = '', $conf = array())
3538  {
3539  return $this->wrapSpace($content, trim($conf['spaceBefore']) . '|', $conf['space.']);
3540  }
3541 
3551  public function stdWrap_spaceAfter($content = '', $conf = array())
3552  {
3553  return $this->wrapSpace($content, '|' . trim($conf['spaceAfter']), $conf['space.']);
3554  }
3555 
3566  public function stdWrap_space($content = '', $conf = array())
3567  {
3568  return $this->wrapSpace($content, trim($conf['space']), $conf['space.']);
3569  }
3570 
3583  public function stdWrap_wrap($content = '', $conf = array())
3584  {
3585  return $this->wrap($content, $conf['wrap'], $conf['wrap.']['splitChar'] ? $conf['wrap.']['splitChar'] : '|');
3586  }
3587 
3597  public function stdWrap_noTrimWrap($content = '', $conf = array())
3598  {
3599  $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
3600  ? $this->stdWrap($conf['noTrimWrap.']['splitChar'], $conf['noTrimWrap.']['splitChar.'])
3601  : $conf['noTrimWrap.']['splitChar'];
3602  if ($splitChar === null || $splitChar === '') {
3603  $splitChar = '|';
3604  }
3605  $content = $this->noTrimWrap(
3606  $content,
3607  $conf['noTrimWrap'],
3608  $splitChar
3609  );
3610  return $content;
3611  }
3612 
3622  public function stdWrap_wrap2($content = '', $conf = array())
3623  {
3624  return $this->wrap($content, $conf['wrap2'], $conf['wrap2.']['splitChar'] ? $conf['wrap2.']['splitChar'] : '|');
3625  }
3626 
3636  public function stdWrap_dataWrap($content = '', $conf = array())
3637  {
3638  return $this->dataWrap($content, $conf['dataWrap']);
3639  }
3640 
3649  public function stdWrap_prepend($content = '', $conf = array())
3650  {
3651  return $this->cObjGetSingle($conf['prepend'], $conf['prepend.'], '/stdWrap/.prepend') . $content;
3652  }
3653 
3662  public function stdWrap_append($content = '', $conf = array())
3663  {
3664  return $content . $this->cObjGetSingle($conf['append'], $conf['append.'], '/stdWrap/.append');
3665  }
3666 
3676  public function stdWrap_wrap3($content = '', $conf = array())
3677  {
3678  return $this->wrap($content, $conf['wrap3'], $conf['wrap3.']['splitChar'] ? $conf['wrap3.']['splitChar'] : '|');
3679  }
3680 
3689  public function stdWrap_orderedStdWrap($content = '', $conf = array())
3690  {
3691  $sortedKeysArray = TemplateService::sortedKeyList($conf['orderedStdWrap.'], true);
3692  foreach ($sortedKeysArray as $key) {
3693  $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.']);
3694  }
3695  return $content;
3696  }
3697 
3706  public function stdWrap_outerWrap($content = '', $conf = array())
3707  {
3708  return $this->wrap($content, $conf['outerWrap']);
3709  }
3710 
3718  public function stdWrap_insertData($content = '')
3719  {
3720  return $this->insertData($content);
3721  }
3722 
3732  public function stdWrap_offsetWrap($content = '', $conf = array())
3733  {
3734  $controlTable = GeneralUtility::makeInstance(OffsetTableContentObject::class);
3735  if ($conf['offsetWrap.']['tableParams'] || $conf['offsetWrap.']['tableParams.']) {
3736  $controlTable->tableParams = isset($conf['offsetWrap.']['tableParams.'])
3737  ? $this->stdWrap($conf['offsetWrap.']['tableParams'], $conf['offsetWrap.']['tableParams.'])
3738  : $conf['offsetWrap.']['tableParams'];
3739  }
3740  if ($conf['offsetWrap.']['tdParams'] || $conf['offsetWrap.']['tdParams.']) {
3741  $controlTable->tdParams = ' ' . (isset($conf['offsetWrap.']['tdParams.'])
3742  ? $this->stdWrap($conf['offsetWrap.']['tdParams'], $conf['offsetWrap.']['tdParams.'])
3743  : $conf['offsetWrap.']['tdParams']);
3744  }
3745  $content = $controlTable->start($content, $conf['offsetWrap']);
3746  if ($conf['offsetWrap.']['stdWrap.']) {
3747  $content = $this->stdWrap($content, $conf['offsetWrap.']['stdWrap.']);
3748  }
3749  return $content;
3750  }
3751 
3760  public function stdWrap_postUserFunc($content = '', $conf = array())
3761  {
3762  return $this->callUserFunction($conf['postUserFunc'], $conf['postUserFunc.'], $content);
3763  }
3764 
3774  public function stdWrap_postUserFuncInt($content = '', $conf = array())
3775  {
3776  $substKey = 'INT_SCRIPT.' . $this->getTypoScriptFrontendController()->uniqueHash();
3777  $this->getTypoScriptFrontendController()->config['INTincScript'][$substKey] = array(
3778  'content' => $content,
3779  'postUserFunc' => $conf['postUserFuncInt'],
3780  'conf' => $conf['postUserFuncInt.'],
3781  'type' => 'POSTUSERFUNC',
3782  'cObj' => serialize($this)
3783  );
3784  $content = '<!--' . $substKey . '-->';
3785  return $content;
3786  }
3787 
3796  public function stdWrap_prefixComment($content = '', $conf = array())
3797  {
3798  if (!$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'] && !empty($conf['prefixComment'])) {
3799  $content = $this->prefixComment($conf['prefixComment'], array(), $content);
3800  }
3801  return $content;
3802  }
3803 
3812  public function stdWrap_editIcons($content = '', $conf = array())
3813  {
3814  if ($this->getTypoScriptFrontendController()->beUserLogin && $conf['editIcons']) {
3815  if (!is_array($conf['editIcons.'])) {
3816  $conf['editIcons.'] = array();
3817  }
3818  $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
3819  }
3820  return $content;
3821  }
3822 
3831  public function stdWrap_editPanel($content = '', $conf = array())
3832  {
3833  if ($this->getTypoScriptFrontendController()->beUserLogin) {
3834  $content = $this->editPanel($content, $conf['editPanel.']);
3835  }
3836  return $content;
3837  }
3838 
3846  public function stdWrap_cacheStore($content = '', $conf = array())
3847  {
3848  if (!isset($conf['cache.'])) {
3849  return $content;
3850  }
3851  $key = $this->calculateCacheKey($conf['cache.']);
3852  if (empty($key)) {
3853  return $content;
3854  }
3856  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
3857  $tags = $this->calculateCacheTags($conf['cache.']);
3858  $lifetime = $this->calculateCacheLifetime($conf['cache.']);
3859  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'])) {
3860  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'] as $_funcRef) {
3861  $params = array(
3862  'key' => $key,
3863  'content' => $content,
3864  'lifetime' => $lifetime,
3865  'tags' => $tags
3866  );
3867  GeneralUtility::callUserFunction($_funcRef, $params, $this);
3868  }
3869  }
3870  $cacheFrontend->set($key, $content, $tags, $lifetime);
3871  return $content;
3872  }
3873 
3883  public function stdWrap_stdWrapPostProcess($content = '', $conf = array())
3884  {
3885  foreach ($this->stdWrapHookObjects as $hookObject) {
3887  $content = $hookObject->stdWrapPostProcess($content, $conf, $this);
3888  }
3889  return $content;
3890  }
3891 
3899  public function stdWrap_debug($content = '')
3900  {
3901  return '<pre>' . htmlspecialchars($content) . '</pre>';
3902  }
3903 
3912  public function stdWrap_debugFunc($content = '', $conf = array())
3913  {
3914  debug($conf['debugFunc'] == 2 ? array($content) : $content);
3915  return $content;
3916  }
3917 
3925  public function stdWrap_debugData($content = '')
3926  {
3927  debug($this->data, '$cObj->data:');
3928  if (is_array($this->alternativeData)) {
3929  debug($this->alternativeData, '$this->alternativeData');
3930  }
3931  return $content;
3932  }
3933 
3943  public function numRows($conf)
3944  {
3945  $result = false;
3946  $conf['select.']['selectFields'] = 'count(*)';
3947  $res = $this->exec_getQuery($conf['table'], $conf['select.']);
3948  $db = $this->getDatabaseConnection();
3949  if ($error = $db->sql_error()) {
3950  $this->getTimeTracker()->setTSlogMessage($error, 3);
3951  } else {
3952  $row = $db->sql_fetch_row($res);
3953  $result = (int)$row[0];
3954  }
3955  $db->sql_free_result($res);
3956  return $result;
3957  }
3958 
3967  public function listNum($content, $listNum, $char)
3968  {
3969  $char = $char ?: ',';
3971  $char = chr($char);
3972  }
3973  $temp = explode($char, $content);
3974  $last = '' . (count($temp) - 1);
3975  // Take a random item if requested
3976  if ($listNum === 'rand') {
3977  $listNum = rand(0, count($temp) - 1);
3978  }
3979  $index = $this->calc(str_ireplace('last', $last, $listNum));
3980  return $temp[$index];
3981  }
3982 
3991  public function checkIf($conf)
3992  {
3993  if (!is_array($conf)) {
3994  return true;
3995  }
3996  if (isset($conf['directReturn'])) {
3997  return (bool)$conf['directReturn'];
3998  }
3999  $flag = true;
4000  if (isset($conf['isNull.'])) {
4001  $isNull = $this->stdWrap('', $conf['isNull.']);
4002  if ($isNull !== null) {
4003  $flag = false;
4004  }
4005  }
4006  if (isset($conf['isTrue']) || isset($conf['isTrue.'])) {
4007  $isTrue = isset($conf['isTrue.']) ? trim($this->stdWrap($conf['isTrue'], $conf['isTrue.'])) : trim($conf['isTrue']);
4008  if (!$isTrue) {
4009  $flag = false;
4010  }
4011  }
4012  if (isset($conf['isFalse']) || isset($conf['isFalse.'])) {
4013  $isFalse = isset($conf['isFalse.']) ? trim($this->stdWrap($conf['isFalse'], $conf['isFalse.'])) : trim($conf['isFalse']);
4014  if ($isFalse) {
4015  $flag = false;
4016  }
4017  }
4018  if (isset($conf['isPositive']) || isset($conf['isPositive.'])) {
4019  $number = isset($conf['isPositive.']) ? $this->calc($this->stdWrap($conf['isPositive'], $conf['isPositive.'])) : $this->calc($conf['isPositive']);
4020  if ($number < 1) {
4021  $flag = false;
4022  }
4023  }
4024  if ($flag) {
4025  $value = isset($conf['value.']) ? trim($this->stdWrap($conf['value'], $conf['value.'])) : trim($conf['value']);
4026  if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
4027  $number = isset($conf['isGreaterThan.']) ? trim($this->stdWrap($conf['isGreaterThan'], $conf['isGreaterThan.'])) : trim($conf['isGreaterThan']);
4028  if ($number <= $value) {
4029  $flag = false;
4030  }
4031  }
4032  if (isset($conf['isLessThan']) || isset($conf['isLessThan.'])) {
4033  $number = isset($conf['isLessThan.']) ? trim($this->stdWrap($conf['isLessThan'], $conf['isLessThan.'])) : trim($conf['isLessThan']);
4034  if ($number >= $value) {
4035  $flag = false;
4036  }
4037  }
4038  if (isset($conf['equals']) || isset($conf['equals.'])) {
4039  $number = isset($conf['equals.']) ? trim($this->stdWrap($conf['equals'], $conf['equals.'])) : trim($conf['equals']);
4040  if ($number != $value) {
4041  $flag = false;
4042  }
4043  }
4044  if (isset($conf['isInList']) || isset($conf['isInList.'])) {
4045  $number = isset($conf['isInList.']) ? trim($this->stdWrap($conf['isInList'], $conf['isInList.'])) : trim($conf['isInList']);
4046  if (!GeneralUtility::inList($value, $number)) {
4047  $flag = false;
4048  }
4049  }
4050  }
4051  if ($conf['negate']) {
4052  $flag = !$flag;
4053  }
4054  return $flag;
4055  }
4056 
4066  public function filelist($data)
4067  {
4068  $data = trim($data);
4069  if (!$data) {
4070  return '';
4071  }
4072  $data_arr = explode('|', $data);
4073  // read directory:
4074  // MUST exist!
4075  $path = '';
4076  if ($this->getTypoScriptFrontendController()->lockFilePath) {
4077  // Cleaning name..., only relative paths accepted.
4078  $path = $this->clean_directory($data_arr[0]);
4079  // See if path starts with lockFilePath, the additional '/' is needed because clean_directory gets rid of it
4080  $path = GeneralUtility::isFirstPartOfStr($path . '/', $this->getTypoScriptFrontendController()->lockFilePath) ? $path : '';
4081  }
4082  if (!$path) {
4083  return '';
4084  }
4085  $items = array(
4086  'files' => array(),
4087  'sorting' => array()
4088  );
4089  $ext_list = strtolower(GeneralUtility::uniqueList($data_arr[1]));
4090  $sorting = trim($data_arr[2]);
4091  // Read dir:
4092  $d = @dir($path);
4093  if (is_object($d)) {
4094  $count = 0;
4095  while ($entry = $d->read()) {
4096  if ($entry != '.' && $entry != '..') {
4097  // Because of odd PHP-error where <br />-tag is sometimes placed after a filename!!
4098  $wholePath = $path . '/' . $entry;
4099  if (file_exists($wholePath) && filetype($wholePath) == 'file') {
4100  $info = GeneralUtility::split_fileref($wholePath);
4101  if (!$ext_list || GeneralUtility::inList($ext_list, $info['fileext'])) {
4102  $items['files'][] = $info['file'];
4103  switch ($sorting) {
4104  case 'name':
4105  $items['sorting'][] = strtolower($info['file']);
4106  break;
4107  case 'size':
4108  $items['sorting'][] = filesize($wholePath);
4109  break;
4110  case 'ext':
4111  $items['sorting'][] = $info['fileext'];
4112  break;
4113  case 'date':
4114  $items['sorting'][] = filectime($wholePath);
4115  break;
4116  case 'mdate':
4117  $items['sorting'][] = filemtime($wholePath);
4118  break;
4119  default:
4120  $items['sorting'][] = $count;
4121  }
4122  $count++;
4123  }
4124  }
4125  }
4126  }
4127  $d->close();
4128  }
4129  // Sort if required
4130  if (!empty($items['sorting'])) {
4131  if (strtolower(trim($data_arr[3])) != 'r') {
4132  asort($items['sorting']);
4133  } else {
4134  arsort($items['sorting']);
4135  }
4136  }
4137  if (!empty($items['files'])) {
4138  // Make list
4139  reset($items['sorting']);
4140  $fullPath = trim($data_arr[4]);
4141  $list_arr = array();
4142  foreach ($items['sorting'] as $key => $v) {
4143  $list_arr[] = $fullPath ? $path . '/' . $items['files'][$key] : $items['files'][$key];
4144  }
4145  return implode(',', $list_arr);
4146  }
4147  return '';
4148  }
4149 
4158  public function clean_directory($theDir)
4159  {
4160  // proceeds if no '//', '..' or '\' is in the $theFile
4161  if (GeneralUtility::validPathStr($theDir)) {
4162  // Removes all dots, slashes and spaces after a path...
4163  $theDir = preg_replace('/[\\/\\. ]*$/', '', $theDir);
4164  if (!GeneralUtility::isAbsPath($theDir) && @is_dir($theDir)) {
4165  return $theDir;
4166  }
4167  }
4168  return '';
4169  }
4170 
4181  public function HTMLparser_TSbridge($theValue, $conf)
4182  {
4183  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
4184  $htmlParserCfg = $htmlParser->HTMLparserConfig($conf);
4185  return $htmlParser->HTMLcleaner($theValue, $htmlParserCfg[0], $htmlParserCfg[1], $htmlParserCfg[2], $htmlParserCfg[3]);
4186  }
4187 
4196  public function dataWrap($content, $wrap)
4197  {
4198  return $this->wrap($content, $this->insertData($wrap));
4199  }
4200 
4209  public function insertData($str)
4210  {
4211  $inside = 0;
4212  $newVal = '';
4213  $pointer = 0;
4214  $totalLen = strlen($str);
4215  do {
4216  if (!$inside) {
4217  $len = strcspn(substr($str, $pointer), '{');
4218  $newVal .= substr($str, $pointer, $len);
4219  $inside = 1;
4220  } else {
4221  $len = strcspn(substr($str, $pointer), '}') + 1;
4222  $newVal .= $this->getData(substr($str, $pointer + 1, $len - 2), $this->data);
4223  $inside = 0;
4224  }
4225  $pointer += $len;
4226  } while ($pointer < $totalLen);
4227  return $newVal;
4228  }
4229 
4240  public function prefixComment($str, $conf, $content)
4241  {
4242  if (empty($str)) {
4243  return $content;
4244  }
4245  $parts = explode('|', $str);
4246  $indent = (int)$parts[0];
4247  $comment = htmlspecialchars($this->insertData($parts[1]));
4248  $output = LF
4249  . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [begin] -->' . LF
4250  . str_pad('', ($indent + 1), TAB) . $content . LF
4251  . str_pad('', $indent, TAB) . '<!-- ' . $comment . ' [end] -->' . LF
4252  . str_pad('', ($indent + 1), TAB);
4253  return $output;
4254  }
4255 
4265  public function substring($content, $options)
4266  {
4267  $tsfe = $this->getTypoScriptFrontendController();
4268  $options = GeneralUtility::intExplode(',', $options . ',');
4269  if ($options[1]) {
4270  return $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $options[0], $options[1]);
4271  } else {
4272  return $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $options[0]);
4273  }
4274  }
4275 
4285  public function crop($content, $options)
4286  {
4287  $options = explode('|', $options);
4288  $chars = (int)$options[0];
4289  $afterstring = trim($options[1]);
4290  $crop2space = trim($options[2]);
4291  if ($chars) {
4292  $tsfe = $this->getTypoScriptFrontendController();
4293  if ($tsfe->csConvObj->strlen($tsfe->renderCharset, $content) > abs($chars)) {
4294  $truncatePosition = false;
4295  if ($chars < 0) {
4296  $content = $tsfe->csConvObj->substr($tsfe->renderCharset, $content, $chars);
4297  if ($crop2space) {
4298  $truncatePosition = strpos($content, ' ');
4299  }
4300  $content = $truncatePosition ? $afterstring . substr($content, $truncatePosition) : $afterstring . $content;
4301  } else {
4302  $content = $tsfe->csConvObj->substr($tsfe->renderCharset, $content, 0, $chars);
4303  if ($crop2space) {
4304  $truncatePosition = strrpos($content, ' ');
4305  }
4306  $content = $truncatePosition ? substr($content, 0, $truncatePosition) . $afterstring : $content . $afterstring;
4307  }
4308  }
4309  }
4310  return $content;
4311  }
4312 
4326  public function cropHTML($content, $options)
4327  {
4328  $options = explode('|', $options);
4329  $chars = (int)$options[0];
4330  $absChars = abs($chars);
4331  $replacementForEllipsis = trim($options[1]);
4332  $crop2space = trim($options[2]) === '1';
4333  // Split $content into an array(even items in the array are outside the tags, odd numbers are tag-blocks).
4334  $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';
4335  $tagsRegEx = '
4336  (
4337  (?:
4338  <!--.*?--> # a comment
4339  |
4340  <canvas[^>]*>.*?</canvas> # a canvas tag
4341  |
4342  <script[^>]*>.*?</script> # a script tag
4343  |
4344  <noscript[^>]*>.*?</noscript> # a noscript tag
4345  |
4346  <template[^>]*>.*?</template> # a template tag
4347  )
4348  |
4349  </?(?:' . $tags . ')+ # opening tag (\'<tag\') or closing tag (\'</tag\')
4350  (?:
4351  (?:
4352  (?:
4353  \\s+\\w[\\w-]* # EITHER spaces, followed by attribute names
4354  (?:
4355  \\s*=?\\s* # equals
4356  (?>
4357  ".*?" # attribute values in double-quotes
4358  |
4359  \'.*?\' # attribute values in single-quotes
4360  |
4361  [^\'">\\s]+ # plain attribute values
4362  )
4363  )?
4364  )
4365  | # OR a single dash (for TYPO3 link tag)
4366  (?:
4367  \\s+-
4368  )
4369  )+\\s*
4370  | # OR only spaces
4371  \\s*
4372  )
4373  /?> # closing the tag with \'>\' or \'/>\'
4374  )';
4375  $splittedContent = preg_split('%' . $tagsRegEx . '%xs', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
4376  // Reverse array if we are cropping from right.
4377  if ($chars < 0) {
4378  $splittedContent = array_reverse($splittedContent);
4379  }
4380  // Crop the text (chars of tag-blocks are not counted).
4381  $strLen = 0;
4382  // This is the offset of the content item which was cropped.
4383  $croppedOffset = null;
4384  $tsfe = $this->getTypoScriptFrontendController();
4385  $countSplittedContent = count($splittedContent);
4386  for ($offset = 0; $offset < $countSplittedContent; $offset++) {
4387  if ($offset % 2 === 0) {
4388  $tempContent = $tsfe->csConvObj->utf8_encode($splittedContent[$offset], $tsfe->renderCharset);
4389  $thisStrLen = $tsfe->csConvObj->strlen('utf-8', html_entity_decode($tempContent, ENT_COMPAT, 'UTF-8'));
4390  if ($strLen + $thisStrLen > $absChars) {
4391  $croppedOffset = $offset;
4392  $cropPosition = $absChars - $strLen;
4393  // The snippet "&[^&\s;]{2,8};" in the RegEx below represents entities.
4394  $patternMatchEntityAsSingleChar = '(&[^&\\s;]{2,8};|.)';
4395  $cropRegEx = $chars < 0 ? '#' . $patternMatchEntityAsSingleChar . '{0,' . ($cropPosition + 1) . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . ($cropPosition + 1) . '}#uis';
4396  if (preg_match($cropRegEx, $tempContent, $croppedMatch)) {
4397  $tempContentPlusOneCharacter = $croppedMatch[0];
4398  } else {
4399  $tempContentPlusOneCharacter = false;
4400  }
4401  $cropRegEx = $chars < 0 ? '#' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}#uis';
4402  if (preg_match($cropRegEx, $tempContent, $croppedMatch)) {
4403  $tempContent = $croppedMatch[0];
4404  if ($crop2space && $tempContentPlusOneCharacter !== false) {
4405  $cropRegEx = $chars < 0 ? '#(?<=\\s)' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}$#uis' : '#^' . $patternMatchEntityAsSingleChar . '{0,' . $cropPosition . '}(?=\\s)#uis';
4406  if (preg_match($cropRegEx, $tempContentPlusOneCharacter, $croppedMatch)) {
4407  $tempContent = $croppedMatch[0];
4408  }
4409  }
4410  }
4411  $splittedContent[$offset] = $tsfe->csConvObj->utf8_decode($tempContent, $tsfe->renderCharset);
4412  break;
4413  } else {
4414  $strLen += $thisStrLen;
4415  }
4416  }
4417  }
4418  // Close cropped tags.
4419  $closingTags = array();
4420  if ($croppedOffset !== null) {
4421  $openingTagRegEx = '#^<(\\w+)(?:\\s|>)#';
4422  $closingTagRegEx = '#^</(\\w+)(?:\\s|>)#';
4423  for ($offset = $croppedOffset - 1; $offset >= 0; $offset = $offset - 2) {
4424  if (substr($splittedContent[$offset], -2) === '/>') {
4425  // Ignore empty element tags (e.g. <br />).
4426  continue;
4427  }
4428  preg_match($chars < 0 ? $closingTagRegEx : $openingTagRegEx, $splittedContent[$offset], $matches);
4429  $tagName = isset($matches[1]) ? $matches[1] : null;
4430  if ($tagName !== null) {
4431  // Seek for the closing (or opening) tag.
4432  $countSplittedContent = count($splittedContent);
4433  for ($seekingOffset = $offset + 2; $seekingOffset < $countSplittedContent; $seekingOffset = $seekingOffset + 2) {
4434  preg_match($chars < 0 ? $openingTagRegEx : $closingTagRegEx, $splittedContent[$seekingOffset], $matches);
4435  $seekingTagName = isset($matches[1]) ? $matches[1] : null;
4436  if ($tagName === $seekingTagName) {
4437  // We found a matching tag.
4438  // Add closing tag only if it occurs after the cropped content item.
4439  if ($seekingOffset > $croppedOffset) {
4440  $closingTags[] = $splittedContent[$seekingOffset];
4441  }
4442  break;
4443  }
4444  }
4445  }
4446  }
4447  // Drop the cropped items of the content array. The $closingTags will be added later on again.
4448  array_splice($splittedContent, $croppedOffset + 1);
4449  }
4450  $splittedContent = array_merge($splittedContent, array(
4451  $croppedOffset !== null ? $replacementForEllipsis : ''
4452  ), $closingTags);
4453  // Reverse array once again if we are cropping from the end.
4454  if ($chars < 0) {
4455  $splittedContent = array_reverse($splittedContent);
4456  }
4457  return implode('', $splittedContent);
4458  }
4459 
4468  public function removeBadHTML($text)
4469  {
4470  // Copyright 2002-2003 Thomas Bley
4471  $text = preg_replace(array(
4472  '\'<script[^>]*?>.*?</script[^>]*?>\'si',
4473  '\'<applet[^>]*?>.*?</applet[^>]*?>\'si',
4474  '\'<object[^>]*?>.*?</object[^>]*?>\'si',
4475  '\'<iframe[^>]*?>.*?</iframe[^>]*?>\'si',
4476  '\'<frameset[^>]*?>.*?</frameset[^>]*?>\'si',
4477  '\'<style[^>]*?>.*?</style[^>]*?>\'si',
4478  '\'<marquee[^>]*?>.*?</marquee[^>]*?>\'si',
4479  '\'<script[^>]*?>\'si',
4480  '\'<meta[^>]*?>\'si',
4481  '\'<base[^>]*?>\'si',
4482  '\'<applet[^>]*?>\'si',
4483  '\'<object[^>]*?>\'si',
4484  '\'<link[^>]*?>\'si',
4485  '\'<iframe[^>]*?>\'si',
4486  '\'<frame[^>]*?>\'si',
4487  '\'<frameset[^>]*?>\'si',
4488  '\'<input[^>]*?>\'si',
4489  '\'<form[^>]*?>\'si',
4490  '\'<embed[^>]*?>\'si',
4491  '\'background-image:url\'si',
4492  '\'<\\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'
4493  ), '', $text);
4494  $text = preg_replace('/<a[^>]*href[[:space:]]*=[[:space:]]*["\']?[[:space:]]*javascript[^>]*/i', '', $text);
4495  // Return clean content
4496  return $text;
4497  }
4498 
4509  public function textStyle($theValue, $conf)
4510  {
4511  $this->getTypoScriptFrontendController()->logDeprecatedTyposcript('textStyle', 'Deprecated since 7.1 and will be removed with CMS 8. Use CSS instead');
4512  $conf['face.'][1] = 'Times New Roman';
4513  $conf['face.'][2] = 'Verdana,Arial,Helvetica,Sans serif';
4514  $conf['face.'][3] = 'Arial,Helvetica,Sans serif';
4515  $conf['size.'][1] = 1;
4516  $conf['size.'][2] = 2;
4517  $conf['size.'][3] = 3;
4518  $conf['size.'][4] = 4;
4519  $conf['size.'][5] = 5;
4520  $conf['size.'][10] = '+1';
4521  $conf['size.'][11] = '-1';
4522  $conf['color.'][240] = 'black';
4523  $conf['color.'][241] = 'white';
4524  $conf['color.'][242] = '#333333';
4525  $conf['color.'][243] = 'gray';
4526  $conf['color.'][244] = 'silver';
4527  $conf['color.'][245] = 'red';
4528  $conf['color.'][246] = 'navy';
4529  $conf['color.'][247] = 'yellow';
4530  $conf['color.'][248] = 'green';
4531  $conf['color.'][249] = 'olive';
4532  $conf['color.'][250] = 'maroon';
4533  $face = $this->data[$conf['face.']['field']];
4534  $size = $this->data[$conf['size.']['field']];
4535  $color = $this->data[$conf['color.']['field']];
4536  $align = $this->data[$conf['align.']['field']];
4537  $properties = $this->data[$conf['properties.']['field']];
4538  if (!$properties) {
4539  $properties = isset($conf['properties.']['default.']) ? $this->stdWrap($conf['properties.']['default'], $conf['properties.']['default.']) : $conf['properties.']['default'];
4540  }
4541  // Properties
4542  if ($properties & 8) {
4543  $theValue = $this->HTMLcaseshift($theValue, 'upper');
4544  }
4545  if ($properties & 1) {
4546  $theValue = '<strong>' . $theValue . '</strong>';
4547  }
4548  if ($properties & 2) {
4549  $theValue = '<i>' . $theValue . '</i>';
4550  }
4551  if ($properties & 4) {
4552  $theValue = '<u>' . $theValue . '</u>';
4553  }
4554  // Fonttag
4555  $theFace = $conf['face.'][$face];
4556  if (!$theFace) {
4557  $theFace = isset($conf['face.']['default.']) ? $this->stdWrap($conf['face.']['default'], $conf['face.']['default.']) : $conf['face.']['default'];
4558  }
4559  $theSize = $conf['size.'][$size];
4560  if (!$theSize) {
4561  $theSize = isset($conf['size.']['default.']) ? $this->stdWrap($conf['size.']['default'], $conf['size.']['default.']) : $conf['size.']['default'];
4562  }
4563  $theColor = $conf['color.'][$color];
4564  if (!$theColor) {
4565  $theColor = isset($conf['color.']['default.']) ? $this->stdWrap($conf['color.']['default'], $conf['color.']['default.']) : $conf['color.']['default.'];
4566  }
4567  if ($conf['altWrap']) {
4568  $theValue = $this->wrap($theValue, $conf['altWrap']);
4569  } elseif ($theFace || $theSize || $theColor) {
4570  $fontWrap = '<font' . ($theFace ? ' face="' . $theFace . '"' : '') . ($theSize ? ' size="' . $theSize . '"' : '') . ($theColor ? ' color="' . $theColor . '"' : '') . '>|</font>';
4571  $theValue = $this->wrap($theValue, $fontWrap);
4572  }
4573  // Align
4574  if ($align) {
4575  $theValue = $this->wrap($theValue, '<div style="text-align:' . $align . ';">|</div>');
4576  }
4577  // Return
4578  return $theValue;
4579  }
4580 
4591  public function tableStyle($theValue, $conf)
4592  {
4593  $this->getTypoScriptFrontendController()->logDeprecatedTyposcript('tableStyle', 'Deprecated since 7.1 and will be removed with CMS 8. Use CSS instead');
4594  $conf['color.'][240] = 'black';
4595  $conf['color.'][241] = 'white';
4596  $conf['color.'][242] = '#333333';
4597  $conf['color.'][243] = 'gray';
4598  $conf['color.'][244] = 'silver';
4599  $align = isset($conf['align.']) ? $this->stdWrap($conf['align'], $conf['align.']) : $conf['align'];
4600  $border = isset($conf['border.']) ? (int)$this->stdWrap($conf['border'], $conf['border.']) : (int)$conf['border'];
4601  $cellspacing = isset($conf['cellspacing.']) ? (int)$this->stdWrap($conf['cellspacing'], $conf['cellspacing.']) : (int)$conf['cellspacing'];
4602  $cellpadding = isset($conf['cellpadding.']) ? (int)$this->stdWrap($conf['cellpadding'], $conf['cellpadding.']) : (int)$conf['cellpadding'];
4603  $color = $this->data[$conf['color.']['field']];
4604  $theColor = $conf['color.'][$color] ? $conf['color.'][$color] : $conf['color.']['default'];
4605  // Assembling the table tag
4606  $tableTagArray = array(
4607  '<table'
4608  );
4609  $tableTagArray[] = 'border="' . $border . '"';
4610  $tableTagArray[] = 'cellspacing="' . $cellspacing . '"';
4611  $tableTagArray[] = 'cellpadding="' . $cellpadding . '"';
4612  if ($align) {
4613  $tableTagArray[] = 'align="' . $align . '"';
4614  }
4615  if ($theColor) {
4616  $tableTagArray[] = 'bgcolor="' . $theColor . '"';
4617  }
4618  if ($conf['params']) {
4619  $tableTagArray[] = $conf['params'];
4620  }
4621  $tableWrap = implode(' ', $tableTagArray) . '> | </table>';
4622  $theValue = $this->wrap($theValue, $tableWrap);
4623  // return
4624  return $theValue;
4625  }
4626 
4635  public function addParams($content, $conf)
4636  {
4637  // For XHTML compliance.
4638  $lowerCaseAttributes = true;
4639  if (!is_array($conf)) {
4640  return $content;
4641  }
4642  $key = 1;
4643  $parts = explode('<', $content);
4644  if ((int)$conf['_offset']) {
4645  $key = (int)$conf['_offset'] < 0 ? count($parts) + (int)$conf['_offset'] : (int)$conf['_offset'];
4646  }
4647  $subparts = explode('>', $parts[$key]);
4648  if (trim($subparts[0])) {
4649  // Get attributes and name
4650  $attribs = GeneralUtility::get_tag_attributes('<' . $subparts[0] . '>');
4651  list($tagName) = explode(' ', $subparts[0], 2);
4652  // adds/overrides attributes
4653  foreach ($conf as $pkey => $val) {
4654  if (substr($pkey, -1) !== '.' && $pkey[0] !== '_') {
4655  $tmpVal = isset($conf[$pkey . '.']) ? $this->stdWrap($conf[$pkey], $conf[$pkey . '.']) : (string)$val;
4656  if ($lowerCaseAttributes) {
4657  $pkey = strtolower($pkey);
4658  }
4659  if ($tmpVal !== '') {
4660  $attribs[$pkey] = $tmpVal;
4661  }
4662  }
4663  }
4664  // Re-assembles the tag and content
4665  $subparts[0] = trim($tagName . ' ' . GeneralUtility::implodeAttributes($attribs));
4666  $parts[$key] = implode('>', $subparts);
4667  $content = implode('<', $parts);
4668  }
4669  return $content;
4670  }
4671 
4682  public function filelink($theValue, $conf)
4683  {
4684  $conf['path'] = isset($conf['path.']) ? $this->stdWrap($conf['path'], $conf['path.']) : $conf['path'];
4685  $theFile = trim($conf['path']) . $theValue;
4686  if (!@is_file($theFile)) {
4687  return '';
4688  }
4689  $theFileEnc = str_replace('%2F', '/', rawurlencode($theFile));
4690  $title = $conf['title'];
4691  if (isset($conf['title.'])) {
4692  $title = $this->stdWrap($title, $conf['title.']);
4693  }
4694  $target = $conf['target'];
4695  if (isset($conf['target.'])) {
4696  $target = $this->stdWrap($target, $conf['target.']);
4697  }
4698  $tsfe = $this->getTypoScriptFrontendController();
4699 
4700  $typoLinkConf = array(
4701  'parameter' => $theFileEnc,
4702  'fileTarget' => $target,
4703  'title' => $title,
4704  'ATagParams' => $this->getATagParams($conf)
4705  );
4706 
4707  if (isset($conf['typolinkConfiguration.'])) {
4708  $additionalTypoLinkConfiguration = $conf['typolinkConfiguration.'];
4709  // We only allow additional configuration. This is why the generated conf overwrites the additional conf.
4710  ArrayUtility::mergeRecursiveWithOverrule($additionalTypoLinkConfiguration, $typoLinkConf);
4711  $typoLinkConf = $additionalTypoLinkConfiguration;
4712  }
4713 
4714  if (isset($conf['jumpurl']) || isset($conf['jumpurl.'])) {
4715  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.');
4716  if (isset($conf['jumpurl'])) {
4717  $typoLinkConf['jumpurl'] = $conf['jumpurl'];
4718  }
4719  if (isset($conf['jumpurl.'])) {
4720  $typoLinkConf['jumpurl.'] = $conf['jumpurl.'];
4721  }
4722  }
4723 
4724  $theLinkWrap = $this->typoLink('|', $typoLinkConf);
4725  $theSize = filesize($theFile);
4726  $fI = GeneralUtility::split_fileref($theFile);
4727  $icon = '';
4728  if ($conf['icon']) {
4729  $conf['icon.']['path'] = isset($conf['icon.']['path.']) ? $this->stdWrap($conf['icon.']['path'], $conf['icon.']['path.']) : $conf['icon.']['path'];
4730  $iconP = !empty($conf['icon.']['path']) ? $conf['icon.']['path'] : ExtensionManagementUtility::siteRelPath('frontend') . 'Resources/Public/Icons/FileIcons/';
4731  $conf['icon.']['ext'] = isset($conf['icon.']['ext.']) ? $this->stdWrap($conf['icon.']['ext'], $conf['icon.']['ext.']) : $conf['icon.']['ext'];
4732  $iconExt = !empty($conf['icon.']['ext']) ? '.' . $conf['icon.']['ext'] : '.gif';
4733  $icon = @is_file(($iconP . $fI['fileext'] . $iconExt)) ? $iconP . $fI['fileext'] . $iconExt : $iconP . 'default' . $iconExt;
4734  // Checking for images: If image, then return link to thumbnail.
4735  $IEList = isset($conf['icon_image_ext_list.']) ? $this->stdWrap($conf['icon_image_ext_list'], $conf['icon_image_ext_list.']) : $conf['icon_image_ext_list'];
4736  $image_ext_list = str_replace(' ', '', strtolower($IEList));
4737  if ($fI['fileext'] && GeneralUtility::inList($image_ext_list, $fI['fileext'])) {
4738  if ($conf['iconCObject']) {
4739  $icon = $this->cObjGetSingle($conf['iconCObject'], $conf['iconCObject.'], 'iconCObject');
4740  } else {
4741  $notFoundThumb = TYPO3_mainDir . 'sysext/core/Resources/Public/Images/NotFound.gif';
4742  $sizeParts = array(64, 64);
4743  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['thumbnails']) {
4744  // using the File Abstraction Layer to generate a preview image
4745  try {
4747  $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($theFile);
4748  if ($fileObject->isMissing()) {
4749  $icon = $notFoundThumb;
4750  } else {
4751  $fileExtension = $fileObject->getExtension();
4752  if ($fileExtension === 'ttf' || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
4753  if ($conf['icon_thumbSize'] || $conf['icon_thumbSize.']) {
4754  $thumbSize = (isset($conf['icon_thumbSize.']) ? $this->stdWrap($conf['icon_thumbSize'], $conf['icon_thumbSize.']) : $conf['icon_thumbSize']);
4755  $sizeParts = explode('x', $thumbSize);
4756  }
4757  $icon = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array(
4758  'width' => $sizeParts[0],
4759  'height' => $sizeParts[1]
4760  ))->getPublicUrl(true);
4761  }
4762  }
4763  } catch (ResourceDoesNotExistException $exception) {
4764  $icon = $notFoundThumb;
4765  }
4766  } else {
4767  $icon = $notFoundThumb;
4768  }
4769  $urlPrefix = '';
4770  if (parse_url($icon, PHP_URL_HOST) === null) {
4771  $urlPrefix = $tsfe->absRefPrefix;
4772  }
4773  $icon = '<img src="' . htmlspecialchars($urlPrefix . $icon) . '"' .
4774  'width="' . $sizeParts[0] . '" height="' . $sizeParts[1] . '" ' .
4775  $this->getBorderAttr(' border="0"') . '' . $this->getAltParam($conf) . ' />';
4776  }
4777  } else {
4778  $conf['icon.']['widthAttribute'] = isset($conf['icon.']['widthAttribute.']) ? $this->stdWrap($conf['icon.']['widthAttribute'], $conf['icon.']['widthAttribute.']) : $conf['icon.']['widthAttribute'];
4779  $iconWidth = !empty($conf['icon.']['widthAttribute']) ? $conf['icon.']['widthAttribute'] : 18;
4780  $conf['icon.']['heightAttribute'] = isset($conf['icon.']['heightAttribute.']) ? $this->stdWrap($conf['icon.']['heightAttribute'], $conf['icon.']['heightAttribute.']) : $conf['icon.']['heightAttribute'];
4781  $iconHeight = !empty($conf['icon.']['heightAttribute']) ? $conf['icon.']['heightAttribute'] : 16;
4782  $icon = '<img src="' . htmlspecialchars($tsfe->absRefPrefix . $icon) . '" width="' . $iconWidth . '" height="' . $iconHeight . '"' . $this->getBorderAttr(' border="0"') . $this->getAltParam($conf) . ' />';
4783  }
4784  if ($conf['icon_link'] && !$conf['combinedLink']) {
4785  $icon = $this->wrap($icon, $theLinkWrap);
4786  }
4787  $icon = isset($conf['icon.']) ? $this->stdWrap($icon, $conf['icon.']) : $icon;
4788  }
4789  $size = '';
4790  if ($conf['size']) {
4791  $size = isset($conf['size.']) ? $this->stdWrap($theSize, $conf['size.']) : $theSize;
4792  }
4793  // Wrapping file label
4794  if ($conf['removePrependedNumbers']) {
4795  $theValue = preg_replace('/_[0-9][0-9](\\.[[:alnum:]]*)$/', '\\1', $theValue);
4796  }
4797  if (isset($conf['labelStdWrap.'])) {
4798  $theValue = $this->stdWrap($theValue, $conf['labelStdWrap.']);
4799  }
4800  // Wrapping file
4801  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
4802  if ($conf['combinedLink']) {
4803  $theValue = $icon . $theValue;
4804  if ($conf['ATagBeforeWrap']) {
4805  $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
4806  } else {
4807  $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
4808  }
4809  $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
4810  // output
4811  $output = $file . $size;
4812  } else {
4813  if ($conf['ATagBeforeWrap']) {
4814  $theValue = $this->wrap($this->wrap($theValue, $wrap), $theLinkWrap);
4815  } else {
4816  $theValue = $this->wrap($this->wrap($theValue, $theLinkWrap), $wrap);
4817  }
4818  $file = isset($conf['file.']) ? $this->stdWrap($theValue, $conf['file.']) : $theValue;
4819  // output
4820  $output = $icon . $file . $size;
4821  }
4822  if (isset($conf['stdWrap.'])) {
4823  $output = $this->stdWrap($output, $conf['stdWrap.']);
4824  }
4825  return $output;
4826  }
4827 
4835  public function calc($val)
4836  {
4837  $parts = GeneralUtility::splitCalc($val, '+-*/');
4838  $value = 0;
4839  foreach ($parts as $part) {
4840  $theVal = $part[1];
4841  $sign = $part[0];
4842  if ((string)(int)$theVal === (string)$theVal) {
4843  $theVal = (int)$theVal;
4844  } else {
4845  $theVal = 0;
4846  }
4847  if ($sign == '-') {
4848  $value -= $theVal;
4849  }
4850  if ($sign == '+') {
4851  $value += $theVal;
4852  }
4853  if ($sign == '/') {
4854  if ((int)$theVal) {
4855  $value /= (int)$theVal;
4856  }
4857  }
4858  if ($sign == '*') {
4859  $value *= $theVal;
4860  }
4861  }
4862  return $value;
4863  }
4864 
4874  public function calcIntExplode($delim, $string)
4875  {
4876  $temp = explode($delim, $string);
4877  foreach ($temp as $key => $val) {
4878  $temp[$key] = (int)$this->calc($val);
4879  }
4880  return $temp;
4881  }
4882 
4894  public function splitObj($value, $conf)
4895  {
4896  $conf['token'] = isset($conf['token.']) ? $this->stdWrap($conf['token'], $conf['token.']) : $conf['token'];
4897  if ($conf['token'] === '') {
4898  return $value;
4899  }
4900  $valArr = explode($conf['token'], $value);
4901 
4902  // return value directly by returnKey. No further processing
4903  if (!empty($valArr) && (MathUtility::canBeInterpretedAsInteger($conf['returnKey']) || $conf['returnKey.'])) {
4904  $key = isset($conf['returnKey.']) ? (int)$this->stdWrap($conf['returnKey'], $conf['returnKey.']) : (int)$conf['returnKey'];
4905  return isset($valArr[$key]) ? $valArr[$key] : '';
4906  }
4907 
4908  // return the amount of elements. No further processing
4909  if (!empty($valArr) && ($conf['returnCount'] || $conf['returnCount.'])) {
4910  $returnCount = isset($conf['returnCount.']) ? (bool)$this->stdWrap($conf['returnCount'], $conf['returnCount.']) : (bool)$conf['returnCount'];
4911  return $returnCount ? count($valArr) : 0;
4912  }
4913 
4914  // start further processing of the values
4915  $conf['max'] = isset($conf['max.']) ? (int)$this->stdWrap($conf['max'], $conf['max.']) : (int)$conf['max'];
4916  $conf['min'] = isset($conf['min.']) ? (int)$this->stdWrap($conf['min'], $conf['min.']) : (int)$conf['min'];
4917 
4918  // calculate splitCount
4919  $splitCount = count($valArr);
4920  $max = isset($conf['max.']) ? $this->stdWrap($conf['max'], $conf['max.']) : $conf['max'];
4921  if ($max && $splitCount > $max) {
4922  $splitCount = $max;
4923  }
4924  $min = isset($conf['min.']) ? $this->stdWrap($conf['min'], $conf['min.']) : $conf['min'];
4925  if ($min && $splitCount < $min) {
4926  $splitCount = $min;
4927  }
4928  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
4929  $cObjNum = isset($conf['cObjNum.']) ? $this->stdWrap($conf['cObjNum'], $conf['cObjNum.']) : $conf['cObjNum'];
4930  $splitArr = array();
4931  if ($wrap || $cObjNum) {
4932  $splitArr['wrap'] = $wrap;
4933  $splitArr['cObjNum'] = $cObjNum;
4934  $splitArr = $GLOBALS['TSFE']->tmpl->splitConfArray($splitArr, $splitCount);
4935  }
4936  $content = '';
4937  for ($a = 0; $a < $splitCount; $a++) {
4938  $this->getTypoScriptFrontendController()->register['SPLIT_COUNT'] = $a;
4939  $value = '' . $valArr[$a];
4940  $this->data[$this->currentValKey] = $value;
4941  if ($splitArr[$a]['cObjNum']) {
4942  $objName = (int)$splitArr[$a]['cObjNum'];
4943  $value = isset($conf[$objName . '.']) ? $this->stdWrap($this->cObjGet($conf[$objName . '.'], $objName . '.'), $conf[$objName . '.']) : $this->cObjGet($conf[$objName . '.'], $objName . '.');
4944  }
4945  $wrap = isset($splitArr[$a]['wrap.']) ? $this->stdWrap($splitArr[$a]['wrap'], $splitArr[$a]['wrap.']) : $splitArr[$a]['wrap'];
4946  if ($wrap) {
4947  $value = $this->wrap($value, $wrap);
4948  }
4949  $content .= $value;
4950  }
4951  return $content;
4952  }
4953 
4961  protected function replacement($content, array $configuration)
4962  {
4963  // Sorts actions in configuration by numeric index
4964  ksort($configuration, SORT_NUMERIC);
4965  foreach ($configuration as $index => $action) {
4966  // Checks whether we have an valid action and a numeric key ending with a dot ("10.")
4967  if (is_array($action) && substr($index, -1) === '.' && MathUtility::canBeInterpretedAsInteger(substr($index, 0, -1))) {
4968  $content = $this->replacementSingle($content, $action);
4969  }
4970  }
4971  return $content;
4972  }
4973 
4981  protected function replacementSingle($content, array $configuration)
4982  {
4983  if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
4984  // Gets the strings
4985  $search = isset($configuration['search.']) ? $this->stdWrap($configuration['search'], $configuration['search.']) : $configuration['search'];
4986  $replace = isset($configuration['replace.']) ? $this->stdWrap($configuration['replace'], $configuration['replace.']) : $configuration['replace'];
4987  // Determines whether regular expression shall be used
4988  if (isset($configuration['useRegExp']) || $configuration['useRegExp.']) {
4989  $useRegularExpression = isset($configuration['useRegExp.']) ? $this->stdWrap($configuration['useRegExp'], $configuration['useRegExp.']) : $configuration['useRegExp'];
4990  }
4991  // Determines whether replace-pattern uses option-split
4992  if (isset($configuration['useOptionSplitReplace']) || isset($configuration['useOptionSplitReplace.'])) {
4993  $useOptionSplitReplace = isset($configuration['useOptionSplitReplace.']) ? $this->stdWrap($configuration['useOptionSplitReplace'], $configuration['useOptionSplitReplace.']) : $configuration['useOptionSplitReplace'];
4994  }
4995 
4996  // Performs a replacement by preg_replace()
4997  if (isset($useRegularExpression)) {
4998  // Get separator-character which precedes the string and separates search-string from the modifiers
4999  $separator = $search[0];
5000  $startModifiers = strrpos($search, $separator);
5001  if ($separator !== false && $startModifiers > 0) {
5002  $modifiers = substr($search, $startModifiers + 1);
5003  // remove "e" (eval-modifier), which would otherwise allow to run arbitrary PHP-code
5004  $modifiers = str_replace('e', '', $modifiers);
5005  $search = substr($search, 0, ($startModifiers + 1)) . $modifiers;
5006  }
5007  if (empty($useOptionSplitReplace)) {
5008  $content = preg_replace($search, $replace, $content);
5009  } else {
5010  // init for replacement
5011  $splitCount = preg_match_all($search, $content, $matches);
5012  $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray(array($replace), $splitCount);
5013  $replaceCount = 0;
5014 
5015  $replaceCallback = function ($match) use ($replaceArray, $search, &$replaceCount) {
5016  $replaceCount++;
5017  return preg_replace($search, $replaceArray[$replaceCount - 1][0], $match[0]);
5018  };
5019  $content = preg_replace_callback($search, $replaceCallback, $content);
5020  }
5021  } else {
5022  if (empty($useOptionSplitReplace)) {
5023  $content = str_replace($search, $replace, $content);
5024  } else {
5025  // turn search-string into a preg-pattern
5026  $searchPreg = '#' . preg_quote($search, '#') . '#';
5027 
5028  // init for replacement
5029  $splitCount = preg_match_all($searchPreg, $content, $matches);
5030  $replaceArray = $this->getTypoScriptFrontendController()->tmpl->splitConfArray(array($replace), $splitCount);
5031  $replaceCount = 0;
5032 
5033  $replaceCallback = function () use ($replaceArray, $search, &$replaceCount) {
5034  $replaceCount++;
5035  return $replaceArray[$replaceCount - 1][0];
5036  };
5037  $content = preg_replace_callback($searchPreg, $replaceCallback, $content);
5038  }
5039  }
5040  }
5041  return $content;
5042  }
5043 
5052  protected function round($content, array $conf = array())
5053  {
5054  $decimals = isset($conf['decimals.']) ? $this->stdWrap($conf['decimals'], $conf['decimals.']) : $conf['decimals'];
5055  $type = isset($conf['roundType.']) ? $this->stdWrap($conf['roundType'], $conf['roundType.']) : $conf['roundType'];
5056  $floatVal = floatval($content);
5057  switch ($type) {
5058  case 'ceil':
5059  $content = ceil($floatVal);
5060  break;
5061  case 'floor':
5062  $content = floor($floatVal);
5063  break;
5064  case 'round':
5065 
5066  default:
5067  $content = round($floatVal, (int)$decimals);
5068  }
5069  return $content;
5070  }
5071 
5080  public function numberFormat($content, $conf)
5081  {
5082  $decimals = isset($conf['decimals.']) ? $this->stdWrap($conf['decimals'], $conf['decimals.']) : $conf['decimals'];
5083  $dec_point = isset($conf['dec_point.']) ? $this->stdWrap($conf['dec_point'], $conf['dec_point.']) : $conf['dec_point'];
5084  $thousands_sep = isset($conf['thousands_sep.']) ? $this->stdWrap($conf['thousands_sep'], $conf['thousands_sep.']) : $conf['thousands_sep'];
5085  return number_format(floatval($content), $decimals, $dec_point, $thousands_sep);
5086  }
5087 
5107  public function parseFunc($theValue, $conf, $ref = '')
5108  {
5109  // Fetch / merge reference, if any
5110  if ($ref) {
5111  $temp_conf = array(
5112  'parseFunc' => $ref,
5113  'parseFunc.' => $conf
5114  );
5115  $temp_conf = $this->mergeTSRef($temp_conf, 'parseFunc');
5116  $conf = $temp_conf['parseFunc.'];
5117  }
5118  // Process:
5119  if ((string)$conf['externalBlocks'] === '') {
5120  return $this->_parseFunc($theValue, $conf);
5121  }
5122  $tags = strtolower(implode(',', GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
5123  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
5124  $parts = $htmlParser->splitIntoBlock($tags, $theValue);
5125  foreach ($parts as $k => $v) {
5126  if ($k % 2) {
5127  // font:
5128  $tagName = strtolower($htmlParser->getFirstTagName($v));
5129  $cfg = $conf['externalBlocks.'][$tagName . '.'];
5130  if ($cfg['stripNLprev'] || $cfg['stripNL']) {
5131  $parts[$k - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $parts[$k - 1]);
5132  }
5133  if ($cfg['stripNLnext'] || $cfg['stripNL']) {
5134  $parts[$k + 1] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $parts[$k + 1]);
5135  }
5136  }
5137  }
5138  foreach ($parts as $k => $v) {
5139  if ($k % 2) {
5140  $tag = $htmlParser->getFirstTag($v);
5141  $tagName = strtolower($htmlParser->getFirstTagName($v));
5142  $cfg = $conf['externalBlocks.'][$tagName . '.'];
5143  if ($cfg['callRecursive']) {
5144  $parts[$k] = $this->parseFunc($htmlParser->removeFirstAndLastTag($v), $conf);
5145  if (!$cfg['callRecursive.']['dontWrapSelf']) {
5146  if ($cfg['callRecursive.']['alternativeWrap']) {
5147  $parts[$k] = $this->wrap($parts[$k], $cfg['callRecursive.']['alternativeWrap']);
5148  } else {
5149  if (is_array($cfg['callRecursive.']['tagStdWrap.'])) {
5150  $tag = $this->stdWrap($tag, $cfg['callRecursive.']['tagStdWrap.']);
5151  }
5152  $parts[$k] = $tag . $parts[$k] . '</' . $tagName . '>';
5153  }
5154  }
5155  } elseif ($cfg['HTMLtableCells']) {
5156  $rowParts = $htmlParser->splitIntoBlock('tr', $parts[$k]);
5157  foreach ($rowParts as $kk => $vv) {
5158  if ($kk % 2) {
5159  $colParts = $htmlParser->splitIntoBlock('td,th', $vv);
5160  $cc = 0;
5161  foreach ($colParts as $kkk => $vvv) {
5162  if ($kkk % 2) {
5163  $cc++;
5164  $tag = $htmlParser->getFirstTag($vvv);
5165  $tagName = strtolower($htmlParser->getFirstTagName($vvv));
5166  $colParts[$kkk] = $htmlParser->removeFirstAndLastTag($vvv);
5167  if ($cfg['HTMLtableCells.'][$cc . '.']['callRecursive'] || !isset($cfg['HTMLtableCells.'][($cc . '.')]['callRecursive']) && $cfg['HTMLtableCells.']['default.']['callRecursive']) {
5168  if ($cfg['HTMLtableCells.']['addChr10BetweenParagraphs']) {
5169  $colParts[$kkk] = str_replace('</p><p>', '</p>' . LF . '<p>', $colParts[$kkk]);
5170  }
5171  $colParts[$kkk] = $this->parseFunc($colParts[$kkk], $conf);
5172  }
5173  $tagStdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.']) ? $cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.'] : $cfg['HTMLtableCells.']['default.']['tagStdWrap.'];
5174  if (is_array($tagStdWrap)) {
5175  $tag = $this->stdWrap($tag, $tagStdWrap);
5176  }
5177  $stdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['stdWrap.']) ? $cfg['HTMLtableCells.'][$cc . '.']['stdWrap.'] : $cfg['HTMLtableCells.']['default.']['stdWrap.'];
5178  if (is_array($stdWrap)) {
5179  $colParts[$kkk] = $this->stdWrap($colParts[$kkk], $stdWrap);
5180  }
5181  $colParts[$kkk] = $tag . $colParts[$kkk] . '</' . $tagName . '>';
5182  }
5183  }
5184  $rowParts[$kk] = implode('', $colParts);
5185  }
5186  }
5187  $parts[$k] = implode('', $rowParts);
5188  }
5189  if (is_array($cfg['stdWrap.'])) {
5190  $parts[$k] = $this->stdWrap($parts[$k], $cfg['stdWrap.']);
5191  }
5192  } else {
5193  $parts[$k] = $this->_parseFunc($parts[$k], $conf);
5194  }
5195  }
5196  return implode('', $parts);
5197  }
5198 
5208  public function _parseFunc($theValue, $conf)
5209  {
5210  if (!empty($conf['if.']) && !$this->checkIf($conf['if.'])) {
5211  return $theValue;
5212  }
5213  // Indicates that the data is from within a tag.
5214  $inside = 0;
5215  // Pointer to the total string position
5216  $pointer = 0;
5217  // Loaded with the current typo-tag if any.
5218  $currentTag = '';
5219  $stripNL = 0;
5220  $contentAccum = array();
5221  $contentAccumP = 0;
5222  $allowTags = strtolower(str_replace(' ', '', $conf['allowTags']));
5223  $denyTags = strtolower(str_replace(' ', '', $conf['denyTags']));
5224  $totalLen = strlen($theValue);
5225  do {
5226  if (!$inside) {
5227  if (!is_array($currentTag)) {
5228  // These operations should only be performed on code outside the typotags...
5229  // data: this checks that we enter tags ONLY if the first char in the tag is alphanumeric OR '/'
5230  $len_p = 0;
5231  $c = 100;
5232  do {
5233  $len = strcspn(substr($theValue, $pointer + $len_p), '<');
5234  $len_p += $len + 1;
5235  $endChar = ord(strtolower(substr($theValue, $pointer + $len_p, 1)));
5236  $c--;
5237  } while ($c > 0 && $endChar && ($endChar < 97 || $endChar > 122) && $endChar != 47);
5238  $len = $len_p - 1;
5239  } else {
5240  // If we're inside a currentTag, just take it to the end of that tag!
5241  $tempContent = strtolower(substr($theValue, $pointer));
5242  $len = strpos($tempContent, '</' . $currentTag[0]);
5243  if (is_string($len) && !$len) {
5244  $len = strlen($tempContent);
5245  }
5246  }
5247  // $data is the content until the next <tag-start or end is detected.
5248  // In case of a currentTag set, this would mean all data between the start- and end-tags
5249  $data = substr($theValue, $pointer, $len);
5250  if ($data != '') {
5251  if ($stripNL) {
5252  // If the previous tag was set to strip NewLines in the beginning of the next data-chunk.
5253  $data = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $data);
5254  }
5255  // These operations should only be performed on code outside the tags...
5256  if (!is_array($currentTag)) {
5257  // Constants
5258  $tsfe = $this->getTypoScriptFrontendController();
5259  $tmpConstants = $tsfe->tmpl->setup['constants.'];
5260  if ($conf['constants'] && is_array($tmpConstants)) {
5261  foreach ($tmpConstants as $key => $val) {
5262  if (is_string($val)) {
5263  $data = str_replace('###' . $key . '###', $val, $data);
5264  }
5265  }
5266  }
5267  // Short
5268  if (is_array($conf['short.'])) {
5269  $shortWords = $conf['short.'];
5270  krsort($shortWords);
5271  foreach ($shortWords as $key => $val) {
5272  if (is_string($val)) {
5273  $data = str_replace($key, $val, $data);
5274  }
5275  }
5276  }
5277  // stdWrap
5278  if (is_array($conf['plainTextStdWrap.'])) {
5279  $data = $this->stdWrap($data, $conf['plainTextStdWrap.']);
5280  }
5281  // userFunc
5282  if ($conf['userFunc']) {
5283  $data = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $data);
5284  }
5285  // Makelinks: (Before search-words as we need the links to be generated when searchwords go on...!)
5286  if ($conf['makelinks']) {
5287  $data = $this->http_makelinks($data, $conf['makelinks.']['http.']);
5288  $data = $this->mailto_makelinks($data, $conf['makelinks.']['mailto.']);
5289  }
5290  // Search Words:
5291  if ($tsfe->no_cache && $conf['sword'] && is_array($tsfe->sWordList) && $tsfe->sWordRegEx) {
5292  $newstring = '';
5293  do {
5294  $pregSplitMode = 'i';
5295  if (isset($tsfe->config['config']['sword_noMixedCase']) && !empty($tsfe->config['config']['sword_noMixedCase'])) {
5296  $pregSplitMode = '';
5297  }
5298  $pieces = preg_split('/' . $tsfe->sWordRegEx . '/' . $pregSplitMode, $data, 2);
5299  $newstring .= $pieces[0];
5300  $match_len = strlen($data) - (strlen($pieces[0]) + strlen($pieces[1]));
5301  $inTag = false;
5302  if (strstr($pieces[0], '<') || strstr($pieces[0], '>')) {
5303  // Returns TRUE, if a '<' is closer to the string-end than '>'.
5304  // This is the case if we're INSIDE a tag (that could have been
5305  // made by makelinks...) and we must secure, that the inside of a tag is
5306  // not marked up.
5307  $inTag = strrpos($pieces[0], '<') > strrpos($pieces[0], '>');
5308  }
5309  // The searchword:
5310  $match = substr($data, strlen($pieces[0]), $match_len);
5311  if (trim($match) && strlen($match) > 1 && !$inTag) {
5312  $match = $this->wrap($match, $conf['sword']);
5313  }
5314  // Concatenate the Search Word again.
5315  $newstring .= $match;
5316  $data = $pieces[1];
5317  } while ($pieces[1]);
5318  $data = $newstring;
5319  }
5320  }
5321  $contentAccum[$contentAccumP] .= $data;
5322  }
5323  $inside = 1;
5324  } else {
5325  // tags
5326  $len = strcspn(substr($theValue, $pointer), '>') + 1;
5327  $data = substr($theValue, $pointer, $len);
5328  if (StringUtility::endsWith($data, '/>') && !StringUtility::beginsWith($data, '<link ')) {
5329  $tagContent = substr($data, 1, -2);
5330  } else {
5331  $tagContent = substr($data, 1, -1);
5332  }
5333  $tag = explode(' ', trim($tagContent), 2);
5334  $tag[0] = strtolower($tag[0]);
5335  if ($tag[0][0] === '/') {
5336  $tag[0] = substr($tag[0], 1);
5337  $tag['out'] = 1;
5338  }
5339  if ($conf['tags.'][$tag[0]]) {
5340  $treated = 0;
5341  $stripNL = 0;
5342  // in-tag
5343  if (!$currentTag && !$tag['out']) {
5344  // $currentTag (array!) is the tag we are currently processing
5345  $currentTag = $tag;
5346  $contentAccumP++;
5347  $treated = 1;
5348  // in-out-tag: img and other empty tags
5349  if (preg_match('/^(area|base|br|col|hr|img|input|meta|param)$/i', $tag[0])) {
5350  $tag['out'] = 1;
5351  }
5352  }
5353  // out-tag
5354  if ($currentTag[0] == $tag[0] && $tag['out']) {
5355  $theName = $conf['tags.'][$tag[0]];
5356  $theConf = $conf['tags.'][$tag[0] . '.'];
5357  // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
5358  $stripNL = $theConf['stripNL'] ? 1 : 0;
5359  // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
5360  $breakOut = $theConf['breakoutTypoTagContent'] ? 1 : 0;
5361  $this->parameters = array();
5362  if ($currentTag[1]) {
5363  $params = GeneralUtility::get_tag_attributes($currentTag[1]);
5364  if (is_array($params)) {
5365  foreach ($params as $option => $val) {
5366  $this->parameters[strtolower($option)] = $val;
5367  }
5368  }
5369  }
5370  $this->parameters['allParams'] = trim($currentTag[1]);
5371  // Removes NL in the beginning and end of the tag-content AND at the end of the currentTagBuffer.
5372  // $stripNL depends on the configuration of the current tag
5373  if ($stripNL) {
5374  $contentAccum[$contentAccumP - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP - 1]);
5375  $contentAccum[$contentAccumP] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $contentAccum[$contentAccumP]);
5376  $contentAccum[$contentAccumP] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP]);
5377  }
5378  $this->data[$this->currentValKey] = $contentAccum[$contentAccumP];
5379  $newInput = $this->cObjGetSingle($theName, $theConf, '/parseFunc/.tags.' . $tag[0]);
5380  // fetch the content object
5381  $contentAccum[$contentAccumP] = $newInput;
5382  $contentAccumP++;
5383  // If the TypoTag section
5384  if (!$breakOut) {
5385  $contentAccum[$contentAccumP - 2] .= $contentAccum[($contentAccumP - 1)] . $contentAccum[$contentAccumP];
5386  unset($contentAccum[$contentAccumP]);
5387  unset($contentAccum[$contentAccumP - 1]);
5388  $contentAccumP -= 2;
5389  }
5390  unset($currentTag);
5391  $treated = 1;
5392  }
5393  // other tags...
5394  if (!$treated) {
5395  $contentAccum[$contentAccumP] .= $data;
5396  }
5397  } else {
5398  // If a tag was not a typo tag, then it is just added to the content
5399  $stripNL = 0;
5400  if (GeneralUtility::inList($allowTags, $tag[0]) || $denyTags != '*' && !GeneralUtility::inList($denyTags, $tag[0])) {
5401  $contentAccum[$contentAccumP] .= $data;
5402  } else {
5403  $contentAccum[$contentAccumP] .= HTMLSpecialChars($data);
5404  }
5405  }
5406  $inside = 0;
5407  }
5408  $pointer += $len;
5409  } while ($pointer < $totalLen);
5410  // Parsing nonTypoTag content (all even keys):
5411  reset($contentAccum);
5412  $contentAccumCount = count($contentAccum);
5413  for ($a = 0; $a < $contentAccumCount; $a++) {
5414  if ($a % 2 != 1) {
5415  // stdWrap
5416  if (is_array($conf['nonTypoTagStdWrap.'])) {
5417  $contentAccum[$a] = $this->stdWrap($contentAccum[$a], $conf['nonTypoTagStdWrap.']);
5418  }
5419  // userFunc
5420  if ($conf['nonTypoTagUserFunc']) {
5421  $contentAccum[$a] = $this->callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'], $contentAccum[$a]);
5422  }
5423  }
5424  }
5425  return implode('', $contentAccum);
5426  }
5427 
5436  public function encaps_lineSplit($theValue, $conf)
5437  {
5438  $lParts = explode(LF, $theValue);
5439  $encapTags = GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList']), true);
5440  $nonWrappedTag = $conf['nonWrappedTag'];
5441  $defaultAlign = isset($conf['defaultAlign.']) ? trim($this->stdWrap($conf['defaultAlign'], $conf['defaultAlign.'])) : trim($conf['defaultAlign']);
5442  if ((string)$theValue === '') {
5443  return '';
5444  }
5445  $str_content = '';
5446  foreach ($lParts as $k => $l) {
5447  $sameBeginEnd = 0;
5448  $emptyTag = 0;
5449  $l = trim($l);
5450  $attrib = array();
5451  $nWrapped = 0;
5452  $tagName = '';
5453  if ($l[0] === '<' && substr($l, -1) === '>') {
5454  $fwParts = explode('>', substr($l, 1), 2);
5455  list($tagName) = explode(' ', $fwParts[0], 2);
5456  if (!$fwParts[1]) {
5457  if (substr($tagName, -1) == '/') {
5458  $tagName = substr($tagName, 0, -1);
5459  }
5460  if (substr($fwParts[0], -1) == '/') {
5461  $sameBeginEnd = 1;
5462  $emptyTag = 1;
5463  $attrib = GeneralUtility::get_tag_attributes('<' . substr($fwParts[0], 0, -1) . '>');
5464  }
5465  } else {
5466  $backParts = GeneralUtility::revExplode('<', substr($fwParts[1], 0, -1), 2);
5467  $attrib = GeneralUtility::get_tag_attributes('<' . $fwParts[0] . '>');
5468  $str_content = $backParts[0];
5469  $sameBeginEnd = substr(strtolower($backParts[1]), 1, strlen($tagName)) == strtolower($tagName);
5470  }
5471  }
5472  if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
5473  $uTagName = strtoupper($tagName);
5474  $uTagName = strtoupper($conf['remapTag.'][$uTagName] ? $conf['remapTag.'][$uTagName] : $uTagName);
5475  } else {
5476  $uTagName = strtoupper($nonWrappedTag);
5477  // The line will be wrapped: $uTagName should not be an empty tag
5478  $emptyTag = 0;
5479  $str_content = $lParts[$k];
5480  $nWrapped = 1;
5481  $attrib = array();
5482  }
5483  // Wrapping all inner-content:
5484  if (is_array($conf['innerStdWrap_all.'])) {
5485  $str_content = $this->stdWrap($str_content, $conf['innerStdWrap_all.']);
5486  }
5487  if ($uTagName) {
5488  // Setting common attributes
5489  if (is_array($conf['addAttributes.'][$uTagName . '.'])) {
5490  foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
5491  if (!is_array($vv)) {
5492  if ((string)$conf['addAttributes.'][($uTagName . '.')][($kk . '.')]['setOnly'] == 'blank') {
5493  if ((string)$attrib[$kk] === '') {
5494  $attrib[$kk] = $vv;
5495  }
5496  } elseif ((string)$conf['addAttributes.'][($uTagName . '.')][($kk . '.')]['setOnly'] == 'exists') {
5497  if (!isset($attrib[$kk])) {
5498  $attrib[$kk] = $vv;
5499  }
5500  } else {
5501  $attrib[$kk] = $vv;
5502  }
5503  }
5504  }
5505  }
5506  // Wrapping all inner-content:
5507  if (is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
5508  $str_content = $this->stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
5509  }
5510  // Default align
5511  if (!$attrib['align'] && $defaultAlign) {
5512  $attrib['align'] = $defaultAlign;
5513  }
5514  $params = GeneralUtility::implodeAttributes($attrib, 1);
5515  if (!($conf['removeWrapping'] && !($emptyTag && $conf['removeWrapping.']['keepSingleTag']))) {
5516  if ($emptyTag) {
5517  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
5518  } else {
5519  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
5520  }
5521  }
5522  }
5523  if ($nWrapped && $conf['wrapNonWrappedLines']) {
5524  $str_content = $this->wrap($str_content, $conf['wrapNonWrappedLines']);
5525  }
5526  $lParts[$k] = $str_content;
5527  }
5528  return implode(LF, $lParts);
5529  }
5530 
5541  public function http_makelinks($data, $conf)
5542  {
5543  $aTagParams = $this->getATagParams($conf);
5544  $textstr = '';
5545  foreach ([ 'http://', 'https://' ] as $scheme) {
5546  $textpieces = explode($scheme, $data);
5547  $pieces = count($textpieces);
5548  $textstr = $textpieces[0];
5549  for ($i = 1; $i < $pieces; $i++) {
5550  $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
5551  if (trim(substr($textstr, -1)) == '' && $len) {
5552  $lastChar = substr($textpieces[$i], $len - 1, 1);
5553  if (!preg_match('/[A-Za-z0-9\\/#_-]/', $lastChar)) {
5554  $len--;
5555  }
5556  // Included '\/' 3/12
5557  $parts[0] = substr($textpieces[$i], 0, $len);
5558  $parts[1] = substr($textpieces[$i], $len);
5559  $keep = $conf['keep'];
5560  $linkParts = parse_url($scheme . $parts[0]);
5561  $linktxt = '';
5562  if (strstr($keep, 'scheme')) {
5563  $linktxt = $scheme;
5564  }
5565  $linktxt .= $linkParts['host'];
5566  if (strstr($keep, 'path')) {
5567  $linktxt .= $linkParts['path'];
5568  // Added $linkParts['query'] 3/12
5569  if (strstr($keep, 'query') && $linkParts['query']) {
5570  $linktxt .= '?' . $linkParts['query'];
5571  } elseif ($linkParts['path'] == '/') {
5572  $linktxt = substr($linktxt, 0, -1);
5573  }
5574  }
5575  if (isset($conf['extTarget'])) {
5576  if (isset($conf['extTarget.'])) {
5577  $target = $this->stdWrap($conf['extTarget'], $conf['extTarget.']);
5578  } else {
5579  $target = $conf['extTarget'];
5580  }
5581  } else {
5582  $target = $this->getTypoScriptFrontendController()->extTarget;
5583  }
5584 
5585  // check for jump URLs or similar
5586  $linkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_COMMON, $scheme . $parts[0], $conf);
5587 
5588  $res = '<a href="' . htmlspecialchars($linkUrl) . '"' . ($target ? ' target="' . $target . '"' : '') . $aTagParams . $this->extLinkATagParams(('http://' . $parts[0]), 'url') . '>';
5589 
5590  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
5591  if ($conf['ATagBeforeWrap']) {
5592  $res = $res . $this->wrap($linktxt, $wrap) . '</a>';
5593  } else {
5594  $res = $this->wrap($res . $linktxt . '</a>', $wrap);
5595  }
5596  $textstr .= $res . $parts[1];
5597  } else {
5598  $textstr .= $scheme . $textpieces[$i];
5599  }
5600  }
5601  $data = $textstr;
5602  }
5603  return $textstr;
5604  }
5605 
5615  public function mailto_makelinks($data, $conf)
5616  {
5617  // http-split
5618  $aTagParams = $this->getATagParams($conf);
5619  $textpieces = explode('mailto:', $data);
5620  $pieces = count($textpieces);
5621  $textstr = $textpieces[0];
5622  $tsfe = $this->getTypoScriptFrontendController();
5623  for ($i = 1; $i < $pieces; $i++) {
5624  $len = strcspn($textpieces[$i], chr(32) . TAB . CRLF);
5625  if (trim(substr($textstr, -1)) == '' && $len) {
5626  $lastChar = substr($textpieces[$i], $len - 1, 1);
5627  if (!preg_match('/[A-Za-z0-9]/', $lastChar)) {
5628  $len--;
5629  }
5630  $parts[0] = substr($textpieces[$i], 0, $len);
5631  $parts[1] = substr($textpieces[$i], $len);
5632  $linktxt = preg_replace('/\\?.*/', '', $parts[0]);
5633  list($mailToUrl, $linktxt) = $this->getMailTo($parts[0], $linktxt);
5634  $mailToUrl = $tsfe->spamProtectEmailAddresses === 'ascii' ? $mailToUrl : htmlspecialchars($mailToUrl);
5635  $res = '<a href="' . $mailToUrl . '"' . $aTagParams . '>';
5636  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
5637  if ($conf['ATagBeforeWrap']) {
5638  $res = $res . $this->wrap($linktxt, $wrap) . '</a>';
5639  } else {
5640  $res = $this->wrap($res . $linktxt . '</a>', $wrap);
5641  }
5642  $textstr .= $res . $parts[1];
5643  } else {
5644  $textstr .= 'mailto:' . $textpieces[$i];
5645  }
5646  }
5647  return $textstr;
5648  }
5649 
5674  public function getImgResource($file, $fileArray)
5675  {
5676  if (empty($file) && empty($fileArray)) {
5677  return null;
5678  }
5679  if (!is_array($fileArray)) {
5680  $fileArray = (array)$fileArray;
5681  }
5682  $imageResource = null;
5683  $tsfe = $this->getTypoScriptFrontendController();
5684  if ($file === 'GIFBUILDER') {
5686  $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
5687  $gifCreator->init();
5688  $theImage = '';
5689  if ($GLOBALS['TYPO3_CONF_VARS']['GFX']['gdlib']) {
5690  $gifCreator->start($fileArray, $this->data);
5691  $theImage = $gifCreator->gifBuild();
5692  }
5693  $imageResource = $gifCreator->getImageDimensions($theImage);
5694  $imageResource['origFile'] = $theImage;
5695  } else {
5696  if ($file instanceof File) {
5697  $fileObject = $file;
5698  } elseif ($file instanceof FileReference) {
5699  $fileObject = $file->getOriginalFile();
5700  if (!isset($fileArray['crop'])) {
5701  $fileArray['crop'] = $file->getProperty('crop');
5702  }
5703  } else {
5704  try {
5705  if ($fileArray['import.']) {
5706  $importedFile = trim($this->stdWrap('', $fileArray['import.']));
5707  if (!empty($importedFile)) {
5708  $file = $importedFile;
5709  }
5710  }
5711 
5713  $treatIdAsReference = isset($fileArray['treatIdAsReference.']) ? $this->stdWrap($fileArray['treatIdAsReference'], $fileArray['treatIdAsReference.']) : $fileArray['treatIdAsReference'];
5714  if (!empty($treatIdAsReference)) {
5715  $fileReference = $this->getResourceFactory()->getFileReferenceObject($file);
5716  $fileObject = $fileReference->getOriginalFile();
5717  if (!isset($fileArray['crop'])) {
5718  $fileArray['crop'] = $fileReference->getProperty('crop');
5719  }
5720  } else {
5721  $fileObject = $this->getResourceFactory()->getFileObject($file);
5722  }
5723  } elseif (preg_match('/^(0|[1-9][0-9]*):/', $file)) { // combined identifier
5724  $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
5725  } else {
5726  if (isset($importedFile) && !empty($importedFile) && !empty($fileArray['import'])) {
5727  $file = $fileArray['import'] . $file;
5728  }
5729  $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file);
5730  }
5731  } catch (Exception $exception) {
5733  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
5734  $logger->warning('The image "' . $file . '" could not be found and won\'t be included in frontend output');
5735  return null;
5736  }
5737  }
5738  if ($fileObject instanceof File) {
5739  $processingConfiguration = array();
5740  $processingConfiguration['width'] = isset($fileArray['width.']) ? $this->stdWrap($fileArray['width'], $fileArray['width.']) : $fileArray['width'];
5741  $processingConfiguration['height'] = isset($fileArray['height.']) ? $this->stdWrap($fileArray['height'], $fileArray['height.']) : $fileArray['height'];
5742  $processingConfiguration['fileExtension'] = isset($fileArray['ext.']) ? $this->stdWrap($fileArray['ext'], $fileArray['ext.']) : $fileArray['ext'];
5743  $processingConfiguration['maxWidth'] = isset($fileArray['maxW.']) ? (int)$this->stdWrap($fileArray['maxW'], $fileArray['maxW.']) : (int)$fileArray['maxW'];
5744  $processingConfiguration['maxHeight'] = isset($fileArray['maxH.']) ? (int)$this->stdWrap($fileArray['maxH'], $fileArray['maxH.']) : (int)$fileArray['maxH'];
5745  $processingConfiguration['minWidth'] = isset($fileArray['minW.']) ? (int)$this->stdWrap($fileArray['minW'], $fileArray['minW.']) : (int)$fileArray['minW'];
5746  $processingConfiguration['minHeight'] = isset($fileArray['minH.']) ? (int)$this->stdWrap($fileArray['minH'], $fileArray['minH.']) : (int)$fileArray['minH'];
5747  $processingConfiguration['noScale'] = isset($fileArray['noScale.']) ? $this->stdWrap($fileArray['noScale'], $fileArray['noScale.']) : $fileArray['noScale'];
5748  $processingConfiguration['additionalParameters'] = isset($fileArray['params.']) ? $this->stdWrap($fileArray['params'], $fileArray['params.']) : $fileArray['params'];
5749  $processingConfiguration['frame'] = isset($fileArray['frame.']) ? (int)$this->stdWrap($fileArray['frame'], $fileArray['frame.']) : (int)$fileArray['frame'];
5750  $processingConfiguration['crop'] = isset($fileArray['crop.']) ? $this->stdWrap($fileArray['crop'], $fileArray['crop.']) : (isset($fileArray['crop']) ? $fileArray['crop'] : null);
5751  // Possibility to cancel/force profile extraction
5752  // see $GLOBALS['TYPO3_CONF_VARS']['GFX']['im_stripProfileCommand']
5753  if (isset($fileArray['stripProfile'])) {
5754  $processingConfiguration['stripProfile'] = $fileArray['stripProfile'];
5755  }
5756  // Check if we can handle this type of file for editing
5757  if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
5758  $maskArray = $fileArray['m.'];
5759  // Must render mask images and include in hash-calculating
5760  // - otherwise we cannot be sure the filename is unique for the setup!
5761  if (is_array($maskArray)) {
5762  $mask = $this->getImgResource($maskArray['mask'], $maskArray['mask.']);
5763  $bgImg = $this->getImgResource($maskArray['bgImg'], $maskArray['bgImg.']);
5764  $bottomImg = $this->getImgResource($maskArray['bottomImg'], $maskArray['bottomImg.']);
5765  $bottomImg_mask = $this->getImgResource($maskArray['bottomImg_mask'], $maskArray['bottomImg_mask.']);
5766 
5767  $processingConfiguration['maskImages']['maskImage'] = $mask['processedFile'];
5768  $processingConfiguration['maskImages']['backgroundImage'] = $bgImg['processedFile'];
5769  $processingConfiguration['maskImages']['maskBottomImage'] = $bottomImg['processedFile'];
5770  $processingConfiguration['maskImages']['maskBottomImageMask'] = $bottomImg_mask['processedFile'];
5771  }
5772  $processedFileObject = $fileObject->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
5773  $hash = $processedFileObject->calculateChecksum();
5774  // store info in the TSFE template cache (kept for backwards compatibility)
5775  if ($processedFileObject->isProcessed() && !isset($tsfe->tmpl->fileCache[$hash])) {
5776  $tsfe->tmpl->fileCache[$hash] = array(
5777  0 => $processedFileObject->getProperty('width'),
5778  1 => $processedFileObject->getProperty('height'),
5779  2 => $processedFileObject->getExtension(),
5780  3 => $processedFileObject->getPublicUrl(),
5781  'origFile' => $fileObject->getPublicUrl(),
5782  'origFile_mtime' => $fileObject->getModificationTime(),
5783  // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder,
5784  // in order for the setup-array to create a unique filename hash.
5785  'originalFile' => $fileObject,
5786  'processedFile' => $processedFileObject,
5787  'fileCacheHash' => $hash
5788  );
5789  }
5790  $imageResource = $tsfe->tmpl->fileCache[$hash];
5791  }
5792  }
5793  }
5794  // If image was processed by GIFBUILDER:
5795  // ($imageResource indicates that it was processed the regular way)
5796  if (!isset($imageResource)) {
5797  $theImage = $tsfe->tmpl->getFileName($file);
5798  if ($theImage) {
5799  $gifCreator = GeneralUtility::makeInstance(GifBuilder::class);
5801  $gifCreator->init();
5802  $info = $gifCreator->imageMagickConvert($theImage, 'WEB');
5803  $info['origFile'] = $theImage;
5804  // This is needed by \TYPO3\CMS\Frontend\Imaging\GifBuilder, ln 100ff in order for the setup-array to create a unique filename hash.
5805  $info['origFile_mtime'] = @filemtime($theImage);
5806  $imageResource = $info;
5807  }
5808  }
5809  // Hook 'getImgResource': Post-processing of image resources
5810  if (isset($imageResource)) {
5812  foreach ($this->getGetImgResourceHookObjects() as $hookObject) {
5813  $imageResource = $hookObject->getImgResourcePostProcess($file, (array)$fileArray, $imageResource, $this);
5814  }
5815  }
5816  return $imageResource;
5817  }
5818 
5819  /***********************************************
5820  *
5821  * Data retrieval etc.
5822  *
5823  ***********************************************/
5830  public function getFieldVal($field)
5831  {
5832  if (!strstr($field, '//')) {
5833  return $this->data[trim($field)];
5834  } else {
5835  $sections = GeneralUtility::trimExplode('//', $field, true);
5836  foreach ($sections as $k) {
5837  if ((string)$this->data[$k] !== '') {
5838  return $this->data[$k];
5839  }
5840  }
5841  }
5842  return '';
5843  }
5844 
5853  public function getData($string, $fieldArray = null)
5854  {
5855  $tsfe = $this->getTypoScriptFrontendController();
5856  if (!is_array($fieldArray)) {
5857  $fieldArray = $tsfe->page;
5858  }
5859  $retVal = '';
5860  $sections = explode('//', $string);
5861  foreach ($sections as $secKey => $secVal) {
5862  if ($retVal) {
5863  break;
5864  }
5865  $parts = explode(':', $secVal, 2);
5866  $type = strtolower(trim($parts[0]));
5867  $typesWithOutParameters = array('level', 'date', 'current', 'pagelayout');
5868  $key = trim($parts[1]);
5869  if (($key != '') || in_array($type, $typesWithOutParameters)) {
5870  switch ($type) {
5871  case 'gp':
5872  // Merge GET and POST and get $key out of the merged array
5873  $getPostArray = GeneralUtility::_GET();
5875  $retVal = $this->getGlobal($key, $getPostArray);
5876  break;
5877  case 'tsfe':
5878  $retVal = $this->getGlobal('TSFE|' . $key);
5879  break;
5880  case 'getenv':
5881  $retVal = getenv($key);
5882  break;
5883  case 'getindpenv':
5884  $retVal = $this->getEnvironmentVariable($key);
5885  break;
5886  case 'field':
5887  $retVal = $this->getGlobal($key, $fieldArray);
5888  break;
5889  case 'file':
5890  $retVal = $this->getFileDataKey($key);
5891  break;
5892  case 'parameters':
5893  $retVal = $this->parameters[$key];
5894  break;
5895  case 'register':
5896  $retVal = $tsfe->register[$key];
5897  break;
5898  case 'global':
5899  $retVal = $this->getGlobal($key);
5900  break;
5901  case 'level':
5902  $retVal = count($tsfe->tmpl->rootLine) - 1;
5903  break;
5904  case 'leveltitle':
5905  $keyParts = GeneralUtility::trimExplode(',', $key);
5906  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5907  $retVal = $this->rootLineValue($numericKey, 'title', strtolower($keyParts[1]) === 'slide');
5908  break;
5909  case 'levelmedia':
5910  $keyParts = GeneralUtility::trimExplode(',', $key);
5911  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5912  $retVal = $this->rootLineValue($numericKey, 'media', strtolower($keyParts[1]) === 'slide');
5913  break;
5914  case 'leveluid':
5915  $numericKey = $this->getKey($key, $tsfe->tmpl->rootLine);
5916  $retVal = $this->rootLineValue($numericKey, 'uid');
5917  break;
5918  case 'levelfield':
5919  $keyParts = GeneralUtility::trimExplode(',', $key);
5920  $numericKey = $this->getKey($keyParts[0], $tsfe->tmpl->rootLine);
5921  $retVal = $this->rootLineValue($numericKey, $keyParts[1], strtolower($keyParts[2]) === 'slide');
5922  break;
5923  case 'fullrootline':
5924  $keyParts = GeneralUtility::trimExplode(',', $key);
5925  $fullKey = (int)$keyParts[0] - count($tsfe->tmpl->rootLine) + count($tsfe->rootLine);
5926  if ($fullKey >= 0) {
5927  $retVal = $this->rootLineValue($fullKey, $keyParts[1], stristr($keyParts[2], 'slide'), $tsfe->rootLine);
5928  }
5929  break;
5930  case 'date':
5931  if (!$key) {
5932  $key = 'd/m Y';
5933  }
5934  $retVal = date($key, $GLOBALS['EXEC_TIME']);
5935  break;
5936  case 'page':
5937  $retVal = $tsfe->page[$key];
5938  break;
5939  case 'pagelayout':
5940  // Check if the current page has a value in the DB field "backend_layout"
5941  // if empty, check the root line for "backend_layout_next_level"
5942  // same as
5943  // field = backend_layout
5944  // ifEmpty.data = levelfield:-2, backend_layout_next_level, slide
5945  // ifEmpty.ifEmpty = default
5946  $retVal = $GLOBALS['TSFE']->page['backend_layout'];
5947 
5948  // If it is set to "none" - don't use any
5949  if ($retVal === '-1') {
5950  $retVal = 'none';
5951  } elseif ($retVal === '' || $retVal === '0') {
5952  // If it not set check the root-line for a layout on next level and use this
5953  // Remove first element, which is the current page
5954  // See also \TYPO3\CMS\Backend\View\BackendLayoutView::getSelectedCombinedIdentifier()
5955  $rootLine = $tsfe->rootLine;
5956  array_shift($rootLine);
5957  foreach ($rootLine as $rootLinePage) {
5958  $retVal = (string) $rootLinePage['backend_layout_next_level'];
5959  // If layout for "next level" is set to "none" - don't use any and stop searching
5960  if ($retVal === '-1') {
5961  $retVal = 'none';
5962  break;
5963  } elseif ($retVal !== '' && $retVal !== '0') {
5964  // Stop searching if a layout for "next level" is set
5965  break;
5966  }
5967  }
5968  }
5969  if ($retVal === '0' || $retVal === '') {
5970  $retVal = 'default';
5971  }
5972  break;
5973  case 'current':
5974  $retVal = $this->data[$this->currentValKey];
5975  break;
5976  case 'db':
5977  $selectParts = GeneralUtility::trimExplode(':', $key);
5978  $db_rec = $tsfe->sys_page->getRawRecord($selectParts[0], $selectParts[1]);
5979  if (is_array($db_rec) && $selectParts[2]) {
5980  $retVal = $db_rec[$selectParts[2]];
5981  }
5982  break;
5983  case 'lll':
5984  $retVal = $tsfe->sL('LLL:' . $key);
5985  break;
5986  case 'path':
5987  $retVal = $tsfe->tmpl->getFileName($key);
5988  break;
5989  case 'cobj':
5990  switch ($key) {
5991  case 'parentRecordNumber':
5992  $retVal = $this->parentRecordNumber;
5993  break;
5994  }
5995  break;
5996  case 'debug':
5997  switch ($key) {
5998  case 'rootLine':
5999  $retVal = DebugUtility::viewArray($tsfe->tmpl->rootLine);
6000  break;
6001  case 'fullRootLine':
6002  $retVal = DebugUtility::viewArray($tsfe->rootLine);
6003  break;
6004  case 'data':
6005  $retVal = DebugUtility::viewArray($this->data);
6006  break;
6007  case 'register':
6008  $retVal = DebugUtility::viewArray($tsfe->register);
6009  break;
6010  case 'page':
6011  $retVal = DebugUtility::viewArray($tsfe->page);
6012  break;
6013  }
6014  break;
6015  }
6016  }
6017  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'])) {
6018  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getData'] as $classData) {
6019  $hookObject = GeneralUtility::getUserObj($classData);
6020  if (!$hookObject instanceof ContentObjectGetDataHookInterface) {
6021  throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetDataHookInterface::class, 1195044480);
6022  }
6023  $retVal = $hookObject->getDataExtension($string, $fieldArray, $secVal, $retVal, $this);
6024  }
6025  }
6026  }
6027  return $retVal;
6028  }
6029 
6039  protected function getFileDataKey($key)
6040  {
6041  $parts = explode(':', $key);
6042  $fileUidOrCurrentKeyword = $parts[0];
6043  $requestedFileInformationKey = $parts[1];
6044  try {
6045  if ($fileUidOrCurrentKeyword === 'current') {
6046  $fileObject = $this->getCurrentFile();
6047  } elseif (MathUtility::canBeInterpretedAsInteger($fileUidOrCurrentKeyword)) {
6049  $fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
6050  $fileObject = $fileFactory->getFileObject($fileUidOrCurrentKeyword);
6051  } else {
6052  $fileObject = null;
6053  }
6054  } catch (Exception $exception) {
6056  $logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
6057  $logger->warning('The file "' . $fileUidOrCurrentKeyword . '" could not be found and won\'t be included in frontend output');
6058  $fileObject = null;
6059  }
6060 
6061  if ($fileObject instanceof FileInterface) {
6062  // All properties of the \TYPO3\CMS\Core\Resource\FileInterface are available here:
6063  switch ($requestedFileInformationKey) {
6064  case 'name':
6065  return $fileObject->getName();
6066  case 'uid':
6067  if (method_exists($fileObject, 'getUid')) {
6068  return $fileObject->getUid();
6069  }
6070  return 0;
6071  case 'originalUid':
6072  if ($fileObject instanceof FileReference) {
6073  return $fileObject->getOriginalFile()->getUid();
6074  }
6075  return null;
6076  case 'size':
6077  return $fileObject->getSize();
6078  case 'sha1':
6079  return $fileObject->getSha1();
6080  case 'extension':
6081  return $fileObject->getExtension();
6082  case 'mimetype':
6083  return $fileObject->getMimeType();
6084  case 'contents':
6085  return $fileObject->getContents();
6086  case 'publicUrl':
6087  return $fileObject->getPublicUrl();
6088  default:
6089  // Generic alternative here
6090  return $fileObject->getProperty($requestedFileInformationKey);
6091  }
6092  } else {
6093  // @todo fail silently as is common in tslib_content
6094  return 'Error: no file object';
6095  }
6096  }
6097 
6109  public function rootLineValue($key, $field, $slideBack = 0, $altRootLine = '')
6110  {
6111  $rootLine = is_array($altRootLine) ? $altRootLine : $this->getTypoScriptFrontendController()->tmpl->rootLine;
6112  if (!$slideBack) {
6113  return $rootLine[$key][$field];
6114  } else {
6115  for ($a = $key; $a >= 0; $a--) {
6116  $val = $rootLine[$a][$field];
6117  if ($val) {
6118  return $val;
6119  }
6120  }
6121  }
6122  return '';
6123  }
6124 
6134  public function getGlobal($keyString, $source = null)
6135  {
6136  $keys = explode('|', $keyString);
6137  $numberOfLevels = count($keys);
6138  $rootKey = trim($keys[0]);
6139  $value = isset($source) ? $source[$rootKey] : $GLOBALS[$rootKey];
6140  for ($i = 1; $i < $numberOfLevels && isset($value); $i++) {
6141  $currentKey = trim($keys[$i]);
6142  if (is_object($value)) {
6143  $value = $value->{$currentKey};
6144  } elseif (is_array($value)) {
6145  $value = $value[$currentKey];
6146  } else {
6147  $value = '';
6148  break;
6149  }
6150  }
6151  if (!is_scalar($value)) {
6152  $value = '';
6153  }
6154  return $value;
6155  }
6156 
6167  public function getKey($key, $arr)
6168  {
6169  $key = (int)$key;
6170  if (is_array($arr)) {
6171  if ($key < 0) {
6172  $key = count($arr) + $key;
6173  }
6174  if ($key < 0) {
6175  $key = 0;
6176  }
6177  }
6178  return $key;
6179  }
6180 
6190  public function TCAlookup($inputValue, $conf)
6191  {
6192  $table = $conf['table'];
6193  $field = $conf['field'];
6194  $delimiter = $conf['delimiter'] ? $conf['delimiter'] : ' ,';
6195  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]) && is_array($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])) {
6196  $tsfe = $this->getTypoScriptFrontendController();
6197  $values = GeneralUtility::trimExplode(',', $inputValue);
6198  $output = array();
6199  foreach ($values as $value) {
6200  // Traverse the items-array...
6201  foreach ($GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] as $item) {
6202  // ... and return the first found label where the value was equal to $key
6203  if ((string)$item[1] === trim($value)) {
6204  $output[] = $tsfe->sL($item[0]);
6205  }
6206  }
6207  }
6208  $returnValue = implode($delimiter, $output);
6209  } else {
6210  $returnValue = $inputValue;
6211  }
6212  return $returnValue;
6213  }
6214 
6215  /***********************************************
6216  *
6217  * Link functions (typolink)
6218  *
6219  ***********************************************/
6220 
6233  protected function resolveMixedLinkParameter($linkText, $mixedLinkParameter, &$configuration = array())
6234  {
6235  $linkParameter = null;
6236 
6237  // Link parameter value = first part
6238  $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
6239 
6240  // Check for link-handler keyword:
6241  list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2);
6242  if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string)$linkHandlerValue !== '') {
6243  $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
6244  if (method_exists($linkHandlerObj, 'main')) {
6245  return $linkHandlerObj->main($linkText, $configuration, $linkHandlerKeyword, $linkHandlerValue, $mixedLinkParameter, $this);
6246  }
6247  }
6248 
6249  // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
6250  if ($linkHandlerKeyword === 'file' && !StringUtility::beginsWith($linkParameterParts['url'], 'file://')) {
6251  try {
6252  $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($linkHandlerValue);
6253  // Link to a folder or file
6254  if ($fileOrFolderObject instanceof File || $fileOrFolderObject instanceof Folder) {
6255  $linkParameter = $fileOrFolderObject->getPublicUrl();
6256  } else {
6257  $linkParameter = null;
6258  }
6259  } catch (\RuntimeException $e) {
6260  // Element wasn't found
6261  $linkParameter = null;
6262  } catch (ResourceDoesNotExistException $e) {
6263  // Resource was not found
6264  return $linkText;
6265  }
6266  } else {
6267  $linkParameter = $linkParameterParts['url'];
6268  }
6269 
6270  // additional parameters that need to be set
6271  if ($linkParameterParts['additionalParams'] !== '') {
6272  $forceParams = $linkParameterParts['additionalParams'];
6273  // params value
6274  $configuration['additionalParams'] .= $forceParams[0] == '&' ? $forceParams : '&' . $forceParams;
6275  }
6276 
6277  return array(
6278  'href' => $linkParameter,
6279  'target' => $linkParameterParts['target'],
6280  'class' => $linkParameterParts['class'],
6281  'title' => $linkParameterParts['title']
6282  );
6283  }
6284 
6299  protected function detectLinkTypeFromLinkParameter($linkParameter)
6300  {
6301  // Parse URL:
6302  $linkParts = parse_url($linkParameter);
6303  // Detecting kind of link:
6304  // If it's a mail address:
6305  if (strpos($linkParameter, '@') > 0 && (!$linkParts['scheme'] || $linkParts['scheme'] == 'mailto')) {
6306  return 'mailto';
6307  }
6308 
6309  $isLocalFile = 0;
6310  $fileChar = intval(strpos($linkParameter, '/'));
6311  $urlChar = intval(strpos($linkParameter, '.'));
6312 
6313  $containsSlash = false;
6314  // Firsts, test if $linkParameter is numeric and page with such id exists. If yes, do not attempt to link to file
6315  if (!MathUtility::canBeInterpretedAsInteger($linkParameter) || empty($this->getTypoScriptFrontendController()->sys_page->getPage_noCheck($linkParameter))) {
6316  // Detects if a file is found in site-root and if so it will be treated like a normal file.
6317  list($rootFileDat) = explode('?', rawurldecode($linkParameter));
6318  $containsSlash = strpos($rootFileDat, '/') !== false;
6319  $rFD_fI = pathinfo($rootFileDat);
6320  if (trim($rootFileDat) && !$containsSlash && (@is_file((PATH_site . $rootFileDat)) || GeneralUtility::inList('php,html,htm', strtolower($rFD_fI['extension'])))) {
6321  $isLocalFile = 1;
6322  } elseif ($containsSlash) {
6323  // Adding this so realurl directories are linked right (non-existing).
6324  $isLocalFile = 2;
6325  }
6326  }
6327 
6328  // url (external): If doubleSlash or if a '.' comes before a '/'.
6329  if ($linkParts['scheme'] || $isLocalFile != 1 && $urlChar && (!$containsSlash || $urlChar < $fileChar)) {
6330  return 'url';
6331 
6332  // file (internal)
6333  } elseif ($containsSlash || $isLocalFile) {
6334  return 'file';
6335  }
6336 
6337  // Integer or alias (alias is without slashes or periods or commas, that is
6338  // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
6339  return 'page';
6340  }
6341 
6356  public function typoLink($linktxt, $conf)
6357  {
6358  $tsfe = $this->getTypoScriptFrontendController();
6359 
6360  $LD = array();
6361  $finalTagParts = array();
6362  $finalTagParts['aTagParams'] = $this->getATagParams($conf);
6363  $linkParameter = isset($conf['parameter.']) ? trim($this->stdWrap($conf['parameter'], $conf['parameter.'])) : trim($conf['parameter']);
6364  $this->lastTypoLinkUrl = '';
6365  $this->lastTypoLinkTarget = '';
6366 
6367  $resolvedLinkParameters = $this->resolveMixedLinkParameter($linktxt, $linkParameter, $conf);
6368  // check if the link handler hook has resolved the link completely already
6369  if (!is_array($resolvedLinkParameters)) {
6370  return $resolvedLinkParameters;
6371  }
6372 
6373  $linkParameter = $resolvedLinkParameters['href'];
6374  $forceTarget = $resolvedLinkParameters['target'];
6375  $linkClass = $resolvedLinkParameters['class'];
6376  $forceTitle = $resolvedLinkParameters['title'];
6377 
6378  if (!$linkParameter) {
6379  return $linktxt;
6380  }
6381 
6382  // Check, if the target is coded as a JS open window link:
6383  $JSwindowParts = array();
6384  $JSwindowParams = '';
6385  if ($forceTarget && preg_match('/^([0-9]+)x([0-9]+)(:(.*)|.*)$/', $forceTarget, $JSwindowParts)) {
6386  // Take all pre-configured and inserted parameters and compile parameter list, including width+height:
6387  $JSwindow_tempParamsArr = GeneralUtility::trimExplode(',', strtolower($conf['JSwindow_params'] . ',' . $JSwindowParts[4]), true);
6388  $JSwindow_paramsArr = array();
6389  foreach ($JSwindow_tempParamsArr as $JSv) {
6390  list($JSp, $JSv) = explode('=', $JSv);
6391  $JSwindow_paramsArr[$JSp] = $JSp . '=' . $JSv;
6392  }
6393  // Add width/height:
6394  $JSwindow_paramsArr['width'] = 'width=' . $JSwindowParts[1];
6395  $JSwindow_paramsArr['height'] = 'height=' . $JSwindowParts[2];
6396  // Imploding into string:
6397  $JSwindowParams = implode(',', $JSwindow_paramsArr);
6398  // Resetting the target since we will use onClick.
6399  $forceTarget = '';
6400  }
6401 
6402  // Internal target:
6403  if ($tsfe->dtdAllowsFrames) {
6404  $target = isset($conf['target']) ? $conf['target'] : $tsfe->intTarget;
6405  } else {
6406  $target = isset($conf['target']) ? $conf['target'] : '';
6407  }
6408  if ($conf['target.']) {
6409  $target = $this->stdWrap($target, $conf['target.']);
6410  }
6411 
6412  // Title tag
6413  $title = $conf['title'];
6414  if ($conf['title.']) {
6415  $title = $this->stdWrap($title, $conf['title.']);
6416  }
6417 
6418  $theTypeP = 0;
6419  // Detecting kind of link
6420  $linkType = $this->detectLinkTypeFromLinkParameter($linkParameter);
6421  switch ($linkType) {
6422  // If it's a mail address
6423  case 'mailto':
6424  $linkParameter = preg_replace('/^mailto:/i', '', $linkParameter);
6425  list($this->lastTypoLinkUrl, $linktxt) = $this->getMailTo($linkParameter, $linktxt);
6426  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6427  break;
6428 
6429  // url (external): If doubleSlash or if a '.' comes before a '/'.
6430  case 'url':
6431  if ($tsfe->dtdAllowsFrames) {
6432  $target = isset($conf['extTarget']) ? $conf['extTarget'] : $tsfe->extTarget;
6433  } else {
6434  $target = isset($conf['extTarget']) ? $conf['extTarget'] : '';
6435  }
6436  if ($conf['extTarget.']) {
6437  $target = $this->stdWrap($target, $conf['extTarget.']);
6438  }
6439  if ($forceTarget) {
6440  $target = $forceTarget;
6441  }
6442  if ($linktxt == '') {
6443  $linktxt = $this->parseFunc($linkParameter, array('makelinks' => 0), '< lib.parseFunc');
6444  }
6445  // Parse URL:
6446  $urlParts = parse_url($linkParameter);
6447  if (!$urlParts['scheme']) {
6448  $scheme = 'http://';
6449  } else {
6450  $scheme = '';
6451  }
6452 
6453  $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_EXTERNAL, $scheme . $linkParameter, $conf);
6454 
6455  $this->lastTypoLinkTarget = $target;
6456  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6457  $finalTagParts['targetParams'] = $target ? ' target="' . $target . '"' : '';
6458  $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], $linkType);
6459  break;
6460 
6461  // file (internal)
6462  case 'file':
6463 
6464  $splitLinkParam = explode('?', $linkParameter);
6465 
6466  // check if the file exists or if a / is contained (same check as in detectLinkType)
6467  if (file_exists(rawurldecode($splitLinkParam[0])) || strpos($linkParameter, '/') !== false) {
6468  if ($linktxt == '') {
6469  $linktxt = $this->parseFunc(rawurldecode($linkParameter), array('makelinks' => 0), '< lib.parseFunc');
6470  }
6471  $this->lastTypoLinkUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_FILE, $GLOBALS['TSFE']->absRefPrefix . $linkParameter, $conf);
6472  $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
6473  $target = isset($conf['fileTarget']) ? $conf['fileTarget'] : $tsfe->fileTarget;
6474  if ($conf['fileTarget.']) {
6475  $target = $this->stdWrap($target, $conf['fileTarget.']);
6476  }
6477  if ($forceTarget) {
6478  $target = $forceTarget;
6479  }
6480  $this->lastTypoLinkTarget = $target;
6481  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6482  $finalTagParts['targetParams'] = $target ? ' target="' . $target . '"' : '';
6483  $finalTagParts['aTagParams'] .= $this->extLinkATagParams($finalTagParts['url'], $linkType);
6484  } else {
6485  $this->getTimeTracker()->setTSlogMessage('typolink(): File \'' . $splitLinkParam[0] . '\' did not exist, so \'' . $linktxt . '\' was not linked.', 1);
6486  return $linktxt;
6487  }
6488  break;
6489 
6490  // Integer or alias (alias is without slashes or periods or commas, that is
6491  // 'nospace,alphanum_x,lower,unique' according to definition in $GLOBALS['TCA']!)
6492  case 'page':
6493  $enableLinksAcrossDomains = $tsfe->config['config']['typolinkEnableLinksAcrossDomains'];
6494 
6495  if ($conf['no_cache.']) {
6496  $conf['no_cache'] = $this->stdWrap($conf['no_cache'], $conf['no_cache.']);
6497  }
6498  // Splitting the parameter by ',' and if the array counts more than 1 element it's an id/type/parameters triplet
6499  $pairParts = GeneralUtility::trimExplode(',', $linkParameter, true);
6500  $linkParameter = $pairParts[0];
6501  $link_params_parts = explode('#', $linkParameter);
6502  // Link-data del
6503  $linkParameter = trim($link_params_parts[0]);
6504  // If no id or alias is given
6505  if ($linkParameter === '') {
6506  $linkParameter = $tsfe->id;
6507  }
6508 
6509  $sectionMark = isset($conf['section.']) ? trim($this->stdWrap($conf['section'], $conf['section.'])) : trim($conf['section']);
6510  $sectionMark = $sectionMark ? (MathUtility::canBeInterpretedAsInteger($sectionMark) ? '#c' : '#') . $sectionMark : '';
6511 
6512  if ($link_params_parts[1] && !$sectionMark) {
6513  $sectionMark = trim($link_params_parts[1]);
6514  $sectionMark = (MathUtility::canBeInterpretedAsInteger($sectionMark) ? '#c' : '#') . $sectionMark;
6515  }
6516  if (count($pairParts) > 1) {
6517  // Overruling 'type'
6518  $theTypeP = isset($pairParts[1]) ? $pairParts[1] : 0;
6519  $conf['additionalParams'] .= isset($pairParts[2]) ? $pairParts[2] : '';
6520  }
6521  // Checking if the id-parameter is an alias.
6522  if (!MathUtility::canBeInterpretedAsInteger($linkParameter)) {
6523  $linkParameter = $tsfe->sys_page->getPageIdFromAlias($linkParameter);
6524  }
6525  // Link to page even if access is missing?
6526  if (isset($conf['linkAccessRestrictedPages'])) {
6527  $disableGroupAccessCheck = (bool)$conf['linkAccessRestrictedPages'];
6528  } else {
6529  $disableGroupAccessCheck = (bool)$tsfe->config['config']['typolinkLinkAccessRestrictedPages'];
6530  }
6531  // Looking up the page record to verify its existence:
6532  $page = $tsfe->sys_page->getPage($linkParameter, $disableGroupAccessCheck);
6533  if (!empty($page)) {
6534  // MointPoints, look for closest MPvar:
6535  $MPvarAcc = array();
6536  if (!$tsfe->config['config']['MP_disableTypolinkClosestMPvalue']) {
6537  $temp_MP = $this->getClosestMPvalueForPage($page['uid'], true);
6538  if ($temp_MP) {
6539  $MPvarAcc['closest'] = $temp_MP;
6540  }
6541  }
6542  // Look for overlay Mount Point:
6543  $mount_info = $tsfe->sys_page->getMountPointInfo($page['uid'], $page);
6544  if (is_array($mount_info) && $mount_info['overlay']) {
6545  $page = $tsfe->sys_page->getPage($mount_info['mount_pid'], $disableGroupAccessCheck);
6546  if (empty($page)) {
6547  $this->getTimeTracker()->setTSlogMessage('typolink(): Mount point \'' . $mount_info['mount_pid'] . '\' was not available, so \'' . $linktxt . '\' was not linked.', 1);
6548  return $linktxt;
6549  }
6550  $MPvarAcc['re-map'] = $mount_info['MPvar'];
6551  }
6552  // Setting title if blank value to link:
6553  if ($linktxt == '') {
6554  $linktxt = $this->parseFunc($page['title'], array('makelinks' => 0), '< lib.parseFunc');
6555  }
6556  // Query Params:
6557  $addQueryParams = $conf['addQueryString'] ? $this->getQueryArguments($conf['addQueryString.']) : '';
6558  $addQueryParams .= isset($conf['additionalParams.']) ? trim($this->stdWrap($conf['additionalParams'], $conf['additionalParams.'])) : trim($conf['additionalParams']);
6559  if ($addQueryParams === '&' || $addQueryParams[0] !== '&') {
6560  $addQueryParams = '';
6561  }
6562  if ($conf['useCacheHash']) {
6563  // Mind the order below! See http://forge.typo3.org/issues/17070
6564  $params = $tsfe->linkVars . $addQueryParams;
6565  if (trim($params, '& ') != '') {
6567  $cacheHash = GeneralUtility::makeInstance(CacheHashCalculator::class);
6568  $cHash = $cacheHash->generateForParameters($params);
6569  $addQueryParams .= $cHash ? '&cHash=' . $cHash : '';
6570  }
6571  unset($params);
6572  }
6573  $targetDomain = '';
6574  $currentDomain = $this->getEnvironmentVariable('HTTP_HOST');
6575  // Mount pages are always local and never link to another domain
6576  if (!empty($MPvarAcc)) {
6577  // Add "&MP" var:
6578  $addQueryParams .= '&MP=' . rawurlencode(implode(',', $MPvarAcc));
6579  } elseif (strpos($addQueryParams, '&MP=') === false && $tsfe->config['config']['typolinkCheckRootline']) {
6580  // We do not come here if additionalParams had '&MP='. This happens when typoLink is called from
6581  // menu. Mount points always work in the content of the current domain and we must not change
6582  // domain if MP variables exist.
6583  // If we link across domains and page is free type shortcut, we must resolve the shortcut first!
6584  // If we do not do it, TYPO3 will fail to (1) link proper page in RealURL/CoolURI because
6585  // they return relative links and (2) show proper page if no RealURL/CoolURI exists when link is clicked
6586  if ($enableLinksAcrossDomains && $page['doktype'] == PageRepository::DOKTYPE_SHORTCUT && $page['shortcut_mode'] == PageRepository::SHORTCUT_MODE_NONE) {
6587  // Save in case of broken destination or endless loop
6588  $page2 = $page;
6589  // Same as in RealURL, seems enough
6590  $maxLoopCount = 20;
6591  while ($maxLoopCount && is_array($page) && $page['doktype'] == PageRepository::DOKTYPE_SHORTCUT && $page['shortcut_mode'] == PageRepository::SHORTCUT_MODE_NONE) {
6592  $page = $tsfe->sys_page->getPage($page['shortcut'], $disableGroupAccessCheck);
6593  $maxLoopCount--;
6594  }
6595  if (empty($page) || $maxLoopCount == 0) {
6596  // We revert if shortcut is broken or maximum number of loops is exceeded (indicates endless loop)
6597  $page = $page2;
6598  }
6599  }
6600 
6601  $targetDomain = $tsfe->getDomainNameForPid($page['uid']);
6602  // Do not prepend the domain if it is the current hostname
6603  if (!$targetDomain || $tsfe->domainNameMatchesCurrentRequest($targetDomain)) {
6604  $targetDomain = '';
6605  }
6606  }
6607  $absoluteUrlScheme = 'http';
6608  // URL shall be absolute:
6609  if (isset($conf['forceAbsoluteUrl']) && $conf['forceAbsoluteUrl'] || $page['url_scheme'] > 0) {
6610  // Override scheme:
6611  if (isset($conf['forceAbsoluteUrl.']['scheme']) && $conf['forceAbsoluteUrl.']['scheme']) {
6612  $absoluteUrlScheme = $conf['forceAbsoluteUrl.']['scheme'];
6613  } elseif ($page['url_scheme'] > 0) {
6614  $absoluteUrlScheme = (int)$page['url_scheme'] === HttpUtility::SCHEME_HTTP ? 'http' : 'https';
6615  } elseif ($this->getEnvironmentVariable('TYPO3_SSL')) {
6616  $absoluteUrlScheme = 'https';
6617  }
6618  // If no domain records are defined, use current domain:
6619  $currentUrlScheme = parse_url($this->getEnvironmentVariable('TYPO3_REQUEST_URL'), PHP_URL_SCHEME);
6620  if ($targetDomain === '' && ($conf['forceAbsoluteUrl'] || $absoluteUrlScheme !== $currentUrlScheme)) {
6621  $targetDomain = $currentDomain;
6622  }
6623  // If go for an absolute link, add site path if it's not taken care about by absRefPrefix
6624  if (!$tsfe->config['config']['absRefPrefix'] && $targetDomain == $currentDomain) {
6625  $targetDomain = $currentDomain . rtrim($this->getEnvironmentVariable('TYPO3_SITE_PATH'), '/');
6626  }
6627  }
6628  // If target page has a different domain and the current domain's linking scheme (e.g. RealURL/...) should not be used
6629  if ($targetDomain !== '' && $targetDomain !== $currentDomain && !$enableLinksAcrossDomains) {
6630  $target = isset($conf['extTarget']) ? $conf['extTarget'] : $tsfe->extTarget;
6631  if ($conf['extTarget.']) {
6632  $target = $this->stdWrap($target, $conf['extTarget.']);
6633  }
6634  if ($forceTarget) {
6635  $target = $forceTarget;
6636  }
6637  $LD['target'] = $target;
6638  // Convert IDNA-like domain (if any)
6639  if (!preg_match('/^[a-z0-9.\\-]*$/i', $targetDomain)) {
6640  $targetDomain = GeneralUtility::idnaEncode($targetDomain);
6641  }
6642  $this->lastTypoLinkUrl = $this->URLqMark(($absoluteUrlScheme . '://' . $targetDomain . '/index.php?id=' . $page['uid']), $addQueryParams) . $sectionMark;
6643  } else {
6644  // Internal link or current domain's linking scheme should be used
6645  if ($forceTarget) {
6646  $target = $forceTarget;
6647  }
6648  $LD = $tsfe->tmpl->linkData($page, $target, $conf['no_cache'], '', '', $addQueryParams, $theTypeP, $targetDomain);
6649  if ($targetDomain !== '') {
6650  // We will add domain only if URL does not have it already.
6651  if ($enableLinksAcrossDomains && $targetDomain !== $currentDomain) {
6652  // Get rid of the absRefPrefix if necessary. absRefPrefix is applicable only
6653  // to the current web site. If we have domain here it means we link across
6654  // domains. absRefPrefix can contain domain name, which will screw up
6655  // the link to the external domain.
6656  $prefixLength = strlen($tsfe->config['config']['absRefPrefix']);
6657  if (substr($LD['totalURL'], 0, $prefixLength) == $tsfe->config['config']['absRefPrefix']) {
6658  $LD['totalURL'] = substr($LD['totalURL'], $prefixLength);
6659  }
6660  }
6661  $urlParts = parse_url($LD['totalURL']);
6662  if ($urlParts['host'] == '') {
6663  $LD['totalURL'] = $absoluteUrlScheme . '://' . $targetDomain . ($LD['totalURL'][0] == '/' ? '' : '/') . $LD['totalURL'];
6664  }
6665  }
6666  $this->lastTypoLinkUrl = $this->URLqMark($LD['totalURL'], '') . $sectionMark;
6667  }
6668  $this->lastTypoLinkTarget = $LD['target'];
6669  $targetPart = $LD['target'] ? ' target="' . htmlspecialchars($LD['target']) . '"' : '';
6670  // 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.
6671  if ($sectionMark && !$tsfe->config['config']['baseURL'] && $page['uid'] == $tsfe->id && !trim($addQueryParams) && !($conf['addQueryString'] && $conf['addQueryString.'])) {
6672  $currentQueryParams = $this->getQueryArguments(array());
6673  if (!trim($currentQueryParams)) {
6674  list(, $URLparams) = explode('?', $this->lastTypoLinkUrl);
6675  list($URLparams) = explode('#', $URLparams);
6676  parse_str($URLparams . $LD['orig_type'], $URLparamsArray);
6677  // Type nums must match as well as page ids
6678  if ((int)$URLparamsArray['type'] == $tsfe->type) {
6679  unset($URLparamsArray['id']);
6680  unset($URLparamsArray['type']);
6681  // If there are no parameters left.... set the new url.
6682  if (empty($URLparamsArray)) {
6683  $this->lastTypoLinkUrl = $sectionMark;
6684  }
6685  }
6686  }
6687  }
6688  // If link is to an access restricted page which should be redirected, then find new URL:
6689  if (empty($conf['linkAccessRestrictedPages']) && $tsfe->config['config']['typolinkLinkAccessRestrictedPages'] && $tsfe->config['config']['typolinkLinkAccessRestrictedPages'] !== 'NONE' && !$tsfe->checkPageGroupAccess($page)) {
6690  $thePage = $tsfe->sys_page->getPage($tsfe->config['config']['typolinkLinkAccessRestrictedPages']);
6691  $addParams = str_replace(
6692  array(
6693  '###RETURN_URL###',
6694  '###PAGE_ID###'
6695  ),
6696  array(
6697  rawurlencode($this->lastTypoLinkUrl),
6698  $page['uid']
6699  ),
6700  $tsfe->config['config']['typolinkLinkAccessRestrictedPages_addParams']
6701  );
6702  $this->lastTypoLinkUrl = $this->getTypoLink_URL($thePage['uid'] . ($theTypeP ? ',' . $theTypeP : ''), $addParams, $target);
6703  $this->lastTypoLinkUrl = $this->forceAbsoluteUrl($this->lastTypoLinkUrl, $conf);
6704  $this->lastTypoLinkLD['totalUrl'] = $this->lastTypoLinkUrl;
6705  $LD = $this->lastTypoLinkLD;
6706  }
6707  // Rendering the tag.
6708  $finalTagParts['url'] = $this->lastTypoLinkUrl;
6709  $finalTagParts['targetParams'] = $targetPart;
6710  } else {
6711  $this->getTimeTracker()->setTSlogMessage('typolink(): Page id \'' . $linkParameter . '\' was not found, so \'' . $linktxt . '\' was not linked.', 1);
6712  return $linktxt;
6713  }
6714  break;
6715  }
6716 
6717  $finalTagParts['TYPE'] = $linkType;
6718  $this->lastTypoLinkLD = $LD;
6719 
6720  if ($forceTitle) {
6721  $title = $forceTitle;
6722  }
6723 
6724  if ($JSwindowParams) {
6725  // Create TARGET-attribute only if the right doctype is used
6726  if (!GeneralUtility::inList('xhtml_strict,xhtml_11,xhtml_2', $tsfe->xhtmlDoctype)) {
6727  $target = ' target="FEopenLink"';
6728  } else {
6729  $target = '';
6730  }
6731  $onClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($finalTagParts['url'])) . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSwindowParams) . ');vHWin.focus();return false;';
6732  $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"' . $target . ' onclick="' . htmlspecialchars($onClick) . '"' . ($title ? ' title="' . htmlspecialchars($title) . '"' : '') . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
6733  } else {
6734  if ($tsfe->spamProtectEmailAddresses === 'ascii' && $linkType === 'mailto') {
6735  $finalAnchorTag = '<a href="' . $finalTagParts['url'] . '"';
6736  } else {
6737  $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"';
6738  }
6739  $finalAnchorTag .= ($title ? ' title="' . htmlspecialchars($title) . '"' : '') . $finalTagParts['targetParams'] . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
6740  }
6741 
6742  // Call user function:
6743  if ($conf['userFunc']) {
6744  $finalTagParts['TAG'] = $finalAnchorTag;
6745  $finalAnchorTag = $this->callUserFunction($conf['userFunc'], $conf['userFunc.'], $finalTagParts);
6746  }
6747 
6748  // Hook: Call post processing function for link rendering:
6749  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'])) {
6750  $_params = array(
6751  'conf' => &$conf,
6752  'linktxt' => &$linktxt,
6753  'finalTag' => &$finalAnchorTag,
6754  'finalTagParts' => &$finalTagParts
6755  );
6756  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typoLink_PostProc'] as $_funcRef) {
6757  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
6758  }
6759  }
6760 
6761  // If flag "returnLastTypoLinkUrl" set, then just return the latest URL made:
6762  if ($conf['returnLast']) {
6763  switch ($conf['returnLast']) {
6764  case 'url':
6765  return $this->lastTypoLinkUrl;
6766  break;
6767  case 'target':
6769  break;
6770  }
6771  }
6772 
6773  $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
6774 
6775  if ($conf['ATagBeforeWrap']) {
6776  return $finalAnchorTag . $this->wrap($linktxt, $wrap) . '</a>';
6777  }
6778  return $this->wrap($finalAnchorTag . $linktxt . '</a>', $wrap);
6779  }
6780 
6788  protected function forceAbsoluteUrl($url, array $configuration)
6789  {
6790  if (!empty($url) && !empty($configuration['forceAbsoluteUrl']) && preg_match('#^(?:([a-z]+)(://)([^/]*)/?)?(.*)$#', $url, $matches)) {
6791  $urlParts = array(
6792  'scheme' => $matches[1],
6793  'delimiter' => '://',
6794  'host' => $matches[3],
6795  'path' => $matches[4]
6796  );
6797  $isUrlModified = false;
6798  // Set scheme and host if not yet part of the URL:
6799  if (empty($urlParts['host'])) {
6800  $urlParts['scheme'] = 'http';
6801  $urlParts['host'] = $this->getEnvironmentVariable('HTTP_HOST');
6802  $urlParts['path'] = '/' . ltrim($urlParts['path'], '/');
6803  // absRefPrefix has been prepended to $url beforehand
6804  // so we only modify the path if no absRefPrefix has been set
6805  // otherwise we would destroy the path
6806  if ($this->getTypoScriptFrontendController()->absRefPrefix === '') {
6807  $urlParts['path'] = $this->getEnvironmentVariable('TYPO3_SITE_PATH') . ltrim($urlParts['path'], '/');
6808  }
6809  $isUrlModified = true;
6810  }
6811  // Override scheme:
6812  $forceAbsoluteUrl = &$configuration['forceAbsoluteUrl.']['scheme'];
6813  if (!empty($forceAbsoluteUrl) && $urlParts['scheme'] !== $forceAbsoluteUrl) {
6814  $urlParts['scheme'] = $forceAbsoluteUrl;
6815  $isUrlModified = true;
6816  }
6817  // Recreate the absolute URL:
6818  if ($isUrlModified) {
6819  $url = implode('', $urlParts);
6820  }
6821  }
6822  return $url;
6823  }
6824 
6832  public function typoLink_URL($conf)
6833  {
6834  $this->typoLink('|', $conf);
6835  return $this->lastTypoLinkUrl;
6836  }
6837 
6851  public function getTypoLink($label, $params, $urlParameters = array(), $target = '')
6852  {
6853  $conf = array();
6854  $conf['parameter'] = $params;
6855  if ($target) {
6856  $conf['target'] = $target;
6857  $conf['extTarget'] = $target;
6858  $conf['fileTarget'] = $target;
6859  }
6860  if (is_array($urlParameters)) {
6861  if (!empty($urlParameters)) {
6862  $conf['additionalParams'] .= GeneralUtility::implodeArrayForUrl('', $urlParameters);
6863  }
6864  } else {
6865  $conf['additionalParams'] .= $urlParameters;
6866  }
6867  $out = $this->typoLink($label, $conf);
6868  return $out;
6869  }
6870 
6878  public function getUrlToCurrentLocation($addQueryString = true)
6879  {
6880  $conf = array();
6881  $conf['parameter'] = $this->getTypoScriptFrontendController()->id . ',' . $this->getTypoScriptFrontendController()->type;
6882  if ($addQueryString) {
6883  $conf['addQueryString'] = '1';
6884  $linkVars = implode(',', array_keys(GeneralUtility::explodeUrl2Array($this->getTypoScriptFrontendController()->linkVars)));
6885  $conf['addQueryString.'] = array(
6886  'method' => 'GET',
6887  'exclude' => 'id,type,cHash' . ($linkVars ? ',' . $linkVars : '')
6888  );
6889  $conf['useCacheHash'] = GeneralUtility::_GET('cHash') ? '1' : '0';
6890  }
6891 
6892  return $this->typoLink_URL($conf);
6893  }
6894 
6904  public function getTypoLink_URL($params, $urlParameters = array(), $target = '')
6905  {
6906  $this->getTypoLink('', $params, $urlParameters, $target);
6907  return $this->lastTypoLinkUrl;
6908  }
6909 
6917  public function typolinkWrap($conf)
6918  {
6919  $k = md5(microtime());
6920  return explode($k, $this->typoLink($k, $conf));
6921  }
6922 
6931  public function currentPageUrl($urlParameters = array(), $id = 0)
6932  {
6933  $tsfe = $this->getTypoScriptFrontendController();
6934  return $this->getTypoLink_URL($id ?: $tsfe->id, $urlParameters, $tsfe->sPre);
6935  }
6936 
6946  protected function processUrl($context, $url, $typolinkConfiguration = array())
6947  {
6948  if (
6949  empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
6950  || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'])
6951  ) {
6952  return $url;
6953  }
6954 
6955  $urlProcessors = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['urlProcessing']['urlProcessors'];
6956  foreach ($urlProcessors as $identifier => $configuration) {
6957  if (empty($configuration) || !is_array($configuration)) {
6958  throw new \RuntimeException('Missing configuration for URI processor "' . $identifier . '".', 1442050529);
6959  }
6960  if (!is_string($configuration['processor']) || empty($configuration['processor']) || !class_exists($configuration['processor']) || !is_subclass_of($configuration['processor'], UrlProcessorInterface::class)) {
6961  throw new \RuntimeException('The URI processor "' . $identifier . '" defines an invalid provider. Ensure the class exists and implements the "' . UrlProcessorInterface::class . '".', 1442050579);
6962  }
6963  }
6964 
6965  $orderedProcessors = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($urlProcessors);
6966  $keepProcessing = true;
6967 
6968  foreach ($orderedProcessors as $configuration) {
6970  $urlProcessor = GeneralUtility::makeInstance($configuration['processor']);
6971  $url = $urlProcessor->process($context, $url, $typolinkConfiguration, $this, $keepProcessing);
6972  if (!$keepProcessing) {
6973  break;
6974  }
6975  }
6976 
6977  return $url;
6978  }
6979 
6989  public function getClosestMPvalueForPage($pageId, $raw = false)
6990  {
6991  $tsfe = $this->getTypoScriptFrontendController();
6992  if (empty($GLOBALS['TYPO3_CONF_VARS']['FE']['enable_mount_pids']) || !$tsfe->MP) {
6993  return '';
6994  }
6995  // MountPoints:
6996  $MP = '';
6997  // Same page as current.
6998  if ((int)$tsfe->id === (int)$pageId) {
6999  $MP = $tsfe->MP;
7000  } else {
7001  // ... otherwise find closest meeting point:
7002  // Gets rootline of linked-to page
7003  $tCR_rootline = $tsfe->sys_page->getRootLine($pageId, '', true);
7004  $inverseTmplRootline = array_reverse($tsfe->tmpl->rootLine);
7005  $rl_mpArray = array();
7006  $startMPaccu = false;
7007  // Traverse root line of link uid and inside of that the REAL root line of current position.
7008  foreach ($tCR_rootline as $tCR_data) {
7009  foreach ($inverseTmplRootline as $rlKey => $invTmplRLRec) {
7010  // Force accumulating when in overlay mode: Links to this page have to stay within the current branch
7011  if ($invTmplRLRec['_MOUNT_OL'] && $tCR_data['uid'] == $invTmplRLRec['uid']) {
7012  $startMPaccu = true;
7013  }
7014  // Accumulate MP data:
7015  if ($startMPaccu && $invTmplRLRec['_MP_PARAM']) {
7016  $rl_mpArray[] = $invTmplRLRec['_MP_PARAM'];
7017  }
7018  // If two PIDs matches and this is NOT the site root, start accumulation of MP data (on the next level):
7019  // (The check for site root is done so links to branches outsite the site but sharing the site roots PID
7020  // is NOT detected as within the branch!)
7021  if ($tCR_data['pid'] == $invTmplRLRec['pid'] && count($inverseTmplRootline) !== $rlKey + 1) {
7022  $startMPaccu = true;
7023  }
7024  }
7025  if ($startMPaccu) {
7026  // Good enough...
7027  break;
7028  }
7029  }
7030  if (!empty($rl_mpArray)) {
7031  $MP = implode(',', array_reverse($rl_mpArray));
7032  }
7033  }
7034  return $raw ? $MP : ($MP ? '&MP=' . rawurlencode($MP) : '');
7035  }
7036 
7046  public function getMailTo($mailAddress, $linktxt)
7047  {
7048  if ((string)$linktxt === '') {
7049  $linktxt = $mailAddress;
7050  }
7051 
7052  $originalMailToUrl = 'mailto:' . $mailAddress;
7053  $mailToUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_MAIL, $originalMailToUrl);
7054 
7055  $tsfe = $this->getTypoScriptFrontendController();
7056  // no processing happened, therefore
7057  if ($mailToUrl === $originalMailToUrl) {
7058  if ($tsfe->spamProtectEmailAddresses) {
7059  if ($tsfe->spamProtectEmailAddresses === 'ascii') {
7060  $mailToUrl = $tsfe->encryptEmail($mailToUrl);
7061  } else {
7062  $mailToUrl = 'javascript:linkTo_UnCryptMailto(' . GeneralUtility::quoteJSvalue($tsfe->encryptEmail($mailToUrl)) . ');';
7063  }
7064  $atLabel = '';
7065  if ($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) {
7066  $atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']);
7067  }
7068  $spamProtectedMailAddress = str_replace('@', $atLabel ? $atLabel : '(at)', $mailAddress);
7069  if ($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']) {
7070  $lastDotLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_lastDotSubst']);
7071  $lastDotLabel = $lastDotLabel ? $lastDotLabel : '(dot)';
7072  $spamProtectedMailAddress = preg_replace('/\\.([^\\.]+)$/', $lastDotLabel . '$1', $spamProtectedMailAddress);
7073  }
7074  $linktxt = str_ireplace($mailAddress, $spamProtectedMailAddress, $linktxt);
7075  }
7076  }
7077 
7078  return array($mailToUrl, $linktxt);
7079  }
7080 
7090  public function getQueryArguments($conf, $overruleQueryArguments = array(), $forceOverruleArguments = false)
7091  {
7092  switch ((string)$conf['method']) {
7093  case 'GET':
7094  $currentQueryArray = GeneralUtility::_GET();
7095  break;
7096  case 'POST':
7097  $currentQueryArray = GeneralUtility::_POST();
7098  break;
7099  case 'GET,POST':
7100  $currentQueryArray = GeneralUtility::_GET();
7102  break;
7103  case 'POST,GET':
7104  $currentQueryArray = GeneralUtility::_POST();
7106  break;
7107  default:
7108  $currentQueryArray = GeneralUtility::explodeUrl2Array($this->getEnvironmentVariable('QUERY_STRING'), true);
7109  }
7110  if ($conf['exclude']) {
7111  $exclude = str_replace(',', '&', $conf['exclude']);
7112  $exclude = GeneralUtility::explodeUrl2Array($exclude, true);
7113  // never repeat id
7114  $exclude['id'] = 0;
7115  $newQueryArray = ArrayUtility::arrayDiffAssocRecursive($currentQueryArray, $exclude);
7116  } else {
7117  $newQueryArray = $currentQueryArray;
7118  }
7119  if ($forceOverruleArguments) {
7120  ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments);
7121  } else {
7122  ArrayUtility::mergeRecursiveWithOverrule($newQueryArray, $overruleQueryArguments, false);
7123  }
7124  return GeneralUtility::implodeArrayForUrl('', $newQueryArray, '', false, true);
7125  }
7126 
7127  /***********************************************
7128  *
7129  * Miscellaneous functions, stand alone
7130  *
7131  ***********************************************/
7143  public function wrap($content, $wrap, $char = '|')
7144  {
7145  if ($wrap) {
7146  $wrapArr = explode($char, $wrap);
7147  $content = trim($wrapArr[0]) . $content . trim($wrapArr[1]);
7148  }
7149  return $content;
7150  }
7151 
7162  public function noTrimWrap($content, $wrap, $char = '|')
7163  {
7164  if ($wrap) {
7165  $wrapArr = explode($char, $wrap);
7166  $content = $wrapArr[1] . $content . $wrapArr[2];
7167  }
7168  return $content;
7169  }
7170 
7179  public function wrapSpace($content, $wrap, array $conf = null)
7180  {
7181  if (trim($wrap)) {
7182  $wrapArray = explode('|', $wrap);
7183  $wrapBefore = (int)$wrapArray[0];
7184  $wrapAfter = (int)$wrapArray[1];
7185  $useDivTag = isset($conf['useDiv']) && $conf['useDiv'];
7186  if ($wrapBefore) {
7187  if ($useDivTag) {
7188  $content = '<div class="content-spacer spacer-before" style="height:' . $wrapBefore . 'px;"></div>' . $content;
7189  } else {
7190  $content = '<span style="width: 1px; height: ' . $wrapBefore . 'px; display: inline-block;"></span><br />' . $content;
7191  }
7192  }
7193  if ($wrapAfter) {
7194  if ($useDivTag) {
7195  $content .= '<div class="content-spacer spacer-after" style="height:' . $wrapAfter . 'px;"></div>';
7196  } else {
7197  $content .= '<span style="width: 1px; height: ' . $wrapAfter . 'px; display: inline-block;"></span><br />';
7198  }
7199  }
7200  }
7201  return $content;
7202  }
7203 
7214  public function callUserFunction($funcName, $conf, $content)
7215  {
7216  // Split parts
7217  $parts = explode('->', $funcName);
7218  if (count($parts) === 2) {
7219  // Class
7220  // Check whether class is available and try to reload includeLibs if possible:
7221  if ($this->isClassAvailable($parts[0], $conf)) {
7222  $classObj = GeneralUtility::makeInstance($parts[0]);
7223  if (is_object($classObj) && method_exists($classObj, $parts[1])) {
7224  $classObj->cObj = $this;
7225  $content = call_user_func_array(array(
7226  $classObj,
7227  $parts[1]
7228  ), array(
7229  $content,
7230  $conf
7231  ));
7232  } else {
7233  $this->getTimeTracker()->setTSlogMessage('Method "' . $parts[1] . '" did not exist in class "' . $parts[0] . '"', 3);
7234  }
7235  } else {
7236  $this->getTimeTracker()->setTSlogMessage('Class "' . $parts[0] . '" did not exist', 3);
7237  }
7238  } elseif (function_exists($funcName)) {
7239  $content = call_user_func($funcName, $content, $conf);
7240  } else {
7241  $this->getTimeTracker()->setTSlogMessage('Function "' . $funcName . '" did not exist', 3);
7242  }
7243  return $content;
7244  }
7245 
7253  public function processParams($params)
7254  {
7255  $paramArr = array();
7256  $lines = GeneralUtility::trimExplode(LF, $params, true);
7257  foreach ($lines as $val) {
7258  $pair = explode('=', $val, 2);
7259  $pair[0] = trim($pair[0]);
7260  if (!GeneralUtility::inList('#,/', $pair[0][0])) {
7261  $paramArr[$pair[0]] = trim($pair[1]);
7262  }
7263  }
7264  return $paramArr;
7265  }
7266 
7273  public function keywords($content)
7274  {
7275  $listArr = preg_split('/[,;' . LF . ']/', $content);
7276  foreach ($listArr as $k => $v) {
7277  $listArr[$k] = trim($v);
7278  }
7279  return implode(',', $listArr);
7280  }
7281 
7290  public function caseshift($theValue, $case)
7291  {
7292  $tsfe = $this->getTypoScriptFrontendController();
7293  switch (strtolower($case)) {
7294  case 'upper':
7295  $theValue = $tsfe->csConvObj->conv_case($tsfe->renderCharset, $theValue, 'toUpper');
7296  break;
7297  case 'lower':
7298  $theValue = $tsfe->csConvObj->conv_case($tsfe->renderCharset, $theValue, 'toLower');
7299  break;
7300  case 'capitalize':
7301  $theValue = ucwords($theValue);
7302  break;
7303  case 'ucfirst':
7304  $theValue = $tsfe->csConvObj->convCaseFirst($tsfe->renderCharset, $theValue, 'toUpper');
7305  break;
7306  case 'lcfirst':
7307  $theValue = $tsfe->csConvObj->convCaseFirst($tsfe->renderCharset, $theValue, 'toLower');
7308  break;
7309  case 'uppercamelcase':
7310  $theValue = GeneralUtility::underscoredToUpperCamelCase($theValue);
7311  break;
7312  case 'lowercamelcase':
7313  $theValue = GeneralUtility::underscoredToLowerCamelCase($theValue);
7314  break;
7315  }
7316  return $theValue;
7317  }
7318 
7327  public function HTMLcaseshift($theValue, $case)
7328  {
7329  $inside = 0;
7330  $newVal = '';
7331  $pointer = 0;
7332  $totalLen = strlen($theValue);
7333  do {
7334  if (!$inside) {
7335  $len = strcspn(substr($theValue, $pointer), '<');
7336  $newVal .= $this->caseshift(substr($theValue, $pointer, $len), $case);
7337  $inside = 1;
7338  } else {
7339  $len = strcspn(substr($theValue, $pointer), '>') + 1;
7340  $newVal .= substr($theValue, $pointer, $len);
7341  $inside = 0;
7342  }
7343  $pointer += $len;
7344  } while ($pointer < $totalLen);
7345  return $newVal;
7346  }
7347 
7355  public function calcAge($seconds, $labels)
7356  {
7358  $labels = ' min| hrs| days| yrs| min| hour| day| year';
7359  } else {
7360  $labels = str_replace('"', '', $labels);
7361  }
7362  $labelArr = explode('|', $labels);
7363  if (count($labelArr) === 4) {
7364  $labelArr = array_merge($labelArr, $labelArr);
7365  }
7366  $absSeconds = abs($seconds);
7367  $sign = $seconds > 0 ? 1 : -1;
7368  if ($absSeconds < 3600) {
7369  $val = round($absSeconds / 60);
7370  $seconds = $sign * $val . ($val == 1 ? $labelArr[4] : $labelArr[0]);
7371  } elseif ($absSeconds < 24 * 3600) {
7372  $val = round($absSeconds / 3600);
7373  $seconds = $sign * $val . ($val == 1 ? $labelArr[5] : $labelArr[1]);
7374  } elseif ($absSeconds < 365 * 24 * 3600) {
7375  $val = round($absSeconds / (24 * 3600));
7376  $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
7377  } else {
7378  $val = round($absSeconds / (365 * 24 * 3600));
7379  $seconds = $sign * $val . ($val == 1 ? $labelArr[7] : $labelArr[3]);
7380  }
7381  return $seconds;
7382  }
7383 
7395  public function sendNotifyEmail($message, $recipients, $cc, $senderAddress, $senderName = '', $replyTo = '')
7396  {
7398  $mail = GeneralUtility::makeInstance(MailMessage::class);
7399  $senderName = trim($senderName);
7400  $senderAddress = trim($senderAddress);
7401  if ($senderName !== '' && $senderAddress !== '') {
7402  $sender = array($senderAddress => $senderName);
7403  } elseif ($senderAddress !== '') {
7404  $sender = array($senderAddress);
7405  } else {
7406  $sender = MailUtility::getSystemFrom();
7407  }
7408  $mail->setFrom($sender);
7409  $parsedReplyTo = MailUtility::parseAddresses($replyTo);
7410  if (!empty($parsedReplyTo)) {
7411  $mail->setReplyTo($parsedReplyTo);
7412  }
7413  $message = trim($message);
7414  if ($message !== '') {
7415  // First line is subject
7416  $messageParts = explode(LF, $message, 2);
7417  $subject = trim($messageParts[0]);
7418  $plainMessage = trim($messageParts[1]);
7419  $parsedRecipients = MailUtility::parseAddresses($recipients);
7420  if (!empty($parsedRecipients)) {
7421  $mail->setTo($parsedRecipients)
7422  ->setSubject($subject)
7423  ->setBody($plainMessage);
7424  $mail->send();
7425  }
7426  $parsedCc = MailUtility::parseAddresses($cc);
7427  if (!empty($parsedCc)) {
7429  $mail = GeneralUtility::makeInstance(MailMessage::class);
7430  if (!empty($parsedReplyTo)) {
7431  $mail->setReplyTo($parsedReplyTo);
7432  }
7433  $mail->setFrom($sender)
7434  ->setTo($parsedCc)
7435  ->setSubject($subject)
7436  ->setBody($plainMessage);
7437  $mail->send();
7438  }
7439  return true;
7440  }
7441  return false;
7442  }
7443 
7451  public function URLqMark($url, $params)
7452  {
7453  if ($params && !strstr($url, '?')) {
7454  return $url . '?' . $params;
7455  } else {
7456  return $url . $params;
7457  }
7458  }
7459 
7468  public function clearTSProperties($TSArr, $propList)
7469  {
7470  $list = explode(',', $propList);
7471  foreach ($list as $prop) {
7472  $prop = trim($prop);
7473  unset($TSArr[$prop]);
7474  unset($TSArr[$prop . '.']);
7475  }
7476  return $TSArr;
7477  }
7478 
7487  public function mergeTSRef($confArr, $prop)
7488  {
7489  if ($confArr[$prop][0] === '<') {
7490  $key = trim(substr($confArr[$prop], 1));
7491  $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
7492  // $name and $conf is loaded with the referenced values.
7493  $old_conf = $confArr[$prop . '.'];
7494  list(, $conf) = $cF->getVal($key, $this->getTypoScriptFrontendController()->tmpl->setup);
7495  if (is_array($old_conf) && !empty($old_conf)) {
7496  $conf = is_array($conf) ? array_replace_recursive($conf, $old_conf) : $old_conf;
7497  }
7498  $confArr[$prop . '.'] = $conf;
7499  }
7500  return $confArr;
7501  }
7502 
7511  public function gifBuilderTextBox($gifbuilderConf, $conf, $text)
7512  {
7513  $chars = (int)$conf['chars'] ?: 20;
7514  $lineDist = (int)$conf['lineDist'] ?: 20;
7515  $Valign = strtolower(trim($conf['Valign']));
7516  $tmplObjNumber = (int)$conf['tmplObjNumber'];
7517  $maxLines = (int)$conf['maxLines'];
7518  if ($tmplObjNumber && $gifbuilderConf[$tmplObjNumber] == 'TEXT') {
7519  $textArr = $this->linebreaks($text, $chars, $maxLines);
7520  $angle = (int)$gifbuilderConf[$tmplObjNumber . '.']['angle'];
7521  foreach ($textArr as $c => $textChunk) {
7522  $index = $tmplObjNumber + 1 + $c * 2;
7523  // Workarea
7524  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
7525  $rad_angle = 2 * pi() / 360 * $angle;
7526  $x_d = sin($rad_angle) * $lineDist;
7527  $y_d = cos($rad_angle) * $lineDist;
7528  $diff_x_d = 0;
7529  $diff_y_d = 0;
7530  if ($Valign == 'center') {
7531  $diff_x_d = $x_d * count($textArr);
7532  $diff_x_d = $diff_x_d / 2;
7533  $diff_y_d = $y_d * count($textArr);
7534  $diff_y_d = $diff_y_d / 2;
7535  }
7536  $x_d = round($x_d * $c - $diff_x_d);
7537  $y_d = round($y_d * $c - $diff_y_d);
7538  $gifbuilderConf[$index] = 'WORKAREA';
7539  $gifbuilderConf[$index . '.']['set'] = $x_d . ',' . $y_d;
7540  // Text
7541  $index++;
7542  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $index);
7543  $gifbuilderConf[$index] = 'TEXT';
7544  $gifbuilderConf[$index . '.'] = $this->clearTSProperties($gifbuilderConf[$tmplObjNumber . '.'], 'text');
7545  $gifbuilderConf[$index . '.']['text'] = $textChunk;
7546  }
7547  $gifbuilderConf = $this->clearTSProperties($gifbuilderConf, $tmplObjNumber);
7548  }
7549  return $gifbuilderConf;
7550  }
7551 
7562  public function linebreaks($string, $chars, $maxLines = 0)
7563  {
7564  $lines = explode(LF, $string);
7565  $lineArr = array();
7566  $c = 0;
7567  foreach ($lines as $paragraph) {
7568  $words = explode(' ', $paragraph);
7569  foreach ($words as $word) {
7570  if (strlen($lineArr[$c] . $word) > $chars) {
7571  $c++;
7572  }
7573  if (!$maxLines || $c < $maxLines) {
7574  $lineArr[$c] .= $word . ' ';
7575  }
7576  }
7577  $c++;
7578  }
7579  return $lineArr;
7580  }
7581 
7588  public function includeLibs(array $config)
7589  {
7590  $librariesIncluded = false;
7591  if (isset($config['includeLibs']) && $config['includeLibs']) {
7592  $libraries = GeneralUtility::trimExplode(',', $config['includeLibs'], true);
7593  $this->getTypoScriptFrontendController()->includeLibraries($libraries);
7594  $librariesIncluded = true;
7595  }
7596  return $librariesIncluded;
7597  }
7598 
7618  protected function isClassAvailable($className, array $config = null)
7619  {
7620  if (class_exists($className)) {
7621  return true;
7622  }
7623  if ($config) {
7624  $pluginConfiguration = &$this->getTypoScriptFrontendController()->tmpl->setup['plugin.'][$className . '.'];
7625  if (isset($pluginConfiguration['includeLibs']) && $pluginConfiguration['includeLibs']) {
7626  $config['includeLibs'] = $pluginConfiguration['includeLibs'];
7627  return $this->includeLibs($config);
7628  }
7629  }
7630  return false;
7631  }
7632 
7633  /***********************************************
7634  *
7635  * Database functions, making of queries
7636  *
7637  ***********************************************/
7648  public function DBgetDelete($table, $uid, $doExec = false)
7649  {
7650  $uid = (int)$uid;
7651  if (!$uid) {
7652  return '';
7653  }
7654  $db = $this->getDatabaseConnection();
7655  if ($GLOBALS['TCA'][$table]['ctrl']['delete']) {
7656  $updateFields = array();
7657  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['delete']] = 1;
7658  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7659  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
7660  }
7661  if ($doExec) {
7662  return $db->exec_UPDATEquery($table, 'uid=' . $uid, $updateFields);
7663  } else {
7664  return $db->UPDATEquery($table, 'uid=' . $uid, $updateFields);
7665  }
7666  } elseif ($doExec) {
7667  return $db->exec_DELETEquery($table, 'uid=' . $uid);
7668  } else {
7669  return $db->DELETEquery($table, 'uid=' . $uid);
7670  }
7671  }
7672 
7687  public function DBgetUpdate($table, $uid, $dataArr, $fieldList, $doExec = false)
7688  {
7689  // uid can never be set
7690  unset($dataArr['uid']);
7691  $uid = (int)$uid;
7692  if ($uid) {
7693  $fieldList = implode(',', GeneralUtility::trimExplode(',', $fieldList, true));
7694  $updateFields = array();
7695  foreach ($dataArr as $f => $v) {
7696  if (GeneralUtility::inList($fieldList, $f)) {
7697  $updateFields[$f] = $v;
7698  }
7699  }
7700  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7701  $updateFields[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
7702  }
7703  if (!empty($updateFields)) {
7704  if ($doExec) {
7705  return $this->getDatabaseConnection()->exec_UPDATEquery($table, 'uid=' . $uid, $updateFields);
7706  }
7707  return $this->getDatabaseConnection()->UPDATEquery($table, 'uid=' . $uid, $updateFields);
7708  }
7709  }
7710  return '';
7711  }
7712 
7727  public function DBgetInsert($table, $pid, $dataArr, $fieldList, $doExec = false)
7728  {
7729  $extraList = 'pid';
7730  if ($GLOBALS['TCA'][$table]['ctrl']['tstamp']) {
7731  $field = $GLOBALS['TCA'][$table]['ctrl']['tstamp'];
7732  $dataArr[$field] = $GLOBALS['EXEC_TIME'];
7733  $extraList .= ',' . $field;
7734  }
7735  if ($GLOBALS['TCA'][$table]['ctrl']['crdate']) {
7736  $field = $GLOBALS['TCA'][$table]['ctrl']['crdate'];
7737  $dataArr[$field] = $GLOBALS['EXEC_TIME'];
7738  $extraList .= ',' . $field;
7739  }
7740  if ($GLOBALS['TCA'][$table]['ctrl']['cruser_id']) {
7741  $field = $GLOBALS['TCA'][$table]['ctrl']['cruser_id'];
7742  $dataArr[$field] = 0;
7743  $extraList .= ',' . $field;
7744  }
7745  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7746  $field = $GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id'];
7747  $dataArr[$field] = (int)$this->getTypoScriptFrontendController()->fe_user->user['uid'];
7748  $extraList .= ',' . $field;
7749  }
7750  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7751  $field = $GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id'];
7752  list($dataArr[$field]) = explode(',', $this->getTypoScriptFrontendController()->fe_user->user['usergroup']);
7753  $dataArr[$field] = (int)$dataArr[$field];
7754  $extraList .= ',' . $field;
7755  }
7756  // Uid can never be set
7757  unset($dataArr['uid']);
7758  if ($pid >= 0) {
7759  $dataArr['pid'] = $pid;
7760  }
7761  // Set pid < 0 and the dataarr-pid will be used!
7762  $fieldList = implode(',', GeneralUtility::trimExplode(',', $fieldList . ',' . $extraList, true));
7763  $insertFields = array();
7764  foreach ($dataArr as $f => $v) {
7765  if (GeneralUtility::inList($fieldList, $f)) {
7766  $insertFields[$f] = $v;
7767  }
7768  }
7769  if ($doExec) {
7770  return $this->getDatabaseConnection()->exec_INSERTquery($table, $insertFields);
7771  } else {
7772  return $this->getDatabaseConnection()->INSERTquery($table, $insertFields);
7773  }
7774  }
7775 
7787  public function DBmayFEUserEdit($table, $row, $feUserRow, $allowedGroups = '', $feEditSelf = 0)
7788  {
7789  $groupList = $allowedGroups ? implode(',', array_intersect(GeneralUtility::trimExplode(',', $feUserRow['usergroup'], true), GeneralUtility::trimExplode(',', $allowedGroups, true))) : $feUserRow['usergroup'];
7790  $ok = 0;
7791  // Points to the field that allows further editing from frontend if not set. If set the record is locked.
7792  if (!$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock'] || !$row[$GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock']]) {
7793  // Points to the field (int) that holds the fe_users-id of the creator fe_user
7794  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7795  $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']];
7796  if ($rowFEUser && $rowFEUser == $feUserRow['uid']) {
7797  $ok = 1;
7798  }
7799  }
7800  // If $feEditSelf is set, fe_users may always edit them selves...
7801  if ($feEditSelf && $table == 'fe_users' && (int)$feUserRow['uid'] === (int)$row['uid']) {
7802  $ok = 1;
7803  }
7804  // Points to the field (int) that holds the fe_group-id of the creator fe_user's first group
7805  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7806  $rowFEUser = (int)$row[$GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']];
7807  if ($rowFEUser) {
7808  if (GeneralUtility::inList($groupList, $rowFEUser)) {
7809  $ok = 1;
7810  }
7811  }
7812  }
7813  }
7814  return $ok;
7815  }
7816 
7829  public function DBmayFEUserEditSelect($table, $feUserRow, $allowedGroups = '', $feEditSelf = 0)
7830  {
7831  // Returns where-definition that selects user-editable records.
7832  $groupList = $allowedGroups ? implode(',', array_intersect(GeneralUtility::trimExplode(',', $feUserRow['usergroup'], true), GeneralUtility::trimExplode(',', $allowedGroups, true))) : $feUserRow['usergroup'];
7833  $OR_arr = array();
7834  // Points to the field (int) that holds the fe_users-id of the creator fe_user
7835  if ($GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id']) {
7836  $OR_arr[] = $GLOBALS['TCA'][$table]['ctrl']['fe_cruser_id'] . '=' . $feUserRow['uid'];
7837  }
7838  // Points to the field (int) that holds the fe_group-id of the creator fe_user's first group
7839  if ($GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id']) {
7840  $values = GeneralUtility::intExplode(',', $groupList);
7841  foreach ($values as $theGroupUid) {
7842  if ($theGroupUid) {
7843  $OR_arr[] = $GLOBALS['TCA'][$table]['ctrl']['fe_crgroup_id'] . '=' . $theGroupUid;
7844  }
7845  }
7846  }
7847  // If $feEditSelf is set, fe_users may always edit them selves...
7848  if ($feEditSelf && $table == 'fe_users') {
7849  $OR_arr[] = 'uid=' . (int)$feUserRow['uid'];
7850  }
7851  $whereDef = ' AND 1=0';
7852  if (!empty($OR_arr)) {
7853  $whereDef = ' AND (' . implode(' OR ', $OR_arr) . ')';
7854  if ($GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock']) {
7855  $whereDef .= ' AND ' . $GLOBALS['TCA'][$table]['ctrl']['fe_admin_lock'] . '=0';
7856  }
7857  }
7858  return $whereDef;
7859  }
7860 
7876  public function enableFields($table, $show_hidden = false, array $ignore_array = array())
7877  {
7878  $tsfe = $this->getTypoScriptFrontendController();
7879  $show_hidden = $show_hidden ?: ($table === 'pages' ? $tsfe->showHiddenPage : $tsfe->showHiddenRecords);
7880  return $tsfe->sys_page->enableFields($table, $show_hidden, $ignore_array);
7881  }
7882 
7909  public function getTreeList($id, $depth, $begin = 0, $dontCheckEnableFields = false, $addSelectFields = '', $moreWhereClauses = '', array $prevId_array = array(), $recursionLevel = 0)
7910  {
7911  $id = (int)$id;
7912  if (!$id) {
7913  return '';
7914  }
7915 
7916  // Init vars:
7917  $allFields = 'uid,hidden,starttime,endtime,fe_group,extendToSubpages,doktype,php_tree_stop,mount_pid,mount_pid_ol,t3ver_state' . $addSelectFields;
7918  $depth = (int)$depth;
7919  $begin = (int)$begin;
7920  $theList = array();
7921  $addId = 0;
7922  $requestHash = '';
7923 
7924  // First level, check id (second level, this is done BEFORE the recursive call)
7925  $db = $this->getDatabaseConnection();
7926  $tsfe = $this->getTypoScriptFrontendController();
7927  if (!$recursionLevel) {
7928  // Check tree list cache
7929  // First, create the hash for this request - not sure yet whether we need all these parameters though
7930  $parameters = array(
7931  $id,
7932  $depth,
7933  $begin,
7934  $dontCheckEnableFields,
7935  $addSelectFields,
7936  $moreWhereClauses,
7937  $prevId_array,
7938  $tsfe->gr_list
7939  );
7940  $requestHash = md5(serialize($parameters));
7941  $cacheEntry = $db->exec_SELECTgetSingleRow(
7942  'treelist',
7943  'cache_treelist',
7944  'md5hash = \'' . $requestHash . '\' AND ( expires > ' . $GLOBALS['EXEC_TIME'] . ' OR expires = 0 )'
7945  );
7946  if (is_array($cacheEntry)) {
7947  // Cache hit
7948  return $cacheEntry['treelist'];
7949  }
7950  // If Id less than zero it means we should add the real id to list:
7951  if ($id < 0) {
7952  $addId = ($id = abs($id));
7953  }
7954  // Check start page:
7955  if ($tsfe->sys_page->getRawRecord('pages', $id, 'uid')) {
7956  // Find mount point if any:
7957  $mount_info = $tsfe->sys_page->getMountPointInfo($id);
7958  if (is_array($mount_info)) {
7959  $id = $mount_info['mount_pid'];
7960  // In Overlay mode, use the mounted page uid as added ID!:
7961  if ($addId && $mount_info['overlay']) {
7962  $addId = $id;
7963  }
7964  }
7965  } else {
7966  // Return blank if the start page was NOT found at all!
7967  return '';
7968  }
7969  }
7970  // Add this ID to the array of IDs
7971  if ($begin <= 0) {
7972  $prevId_array[] = $id;
7973  }
7974  // Select sublevel:
7975  if ($depth > 0) {
7976  $rows = $db->exec_SELECTgetRows(
7977  $allFields,
7978  'pages',
7979  'pid = ' . (int)$id . ' AND deleted = 0 ' . $moreWhereClauses,
7980  '',
7981  'sorting'
7982  );
7983  if (is_array($rows)) {
7984  foreach ($rows as $row) {
7986  $versionState = VersionState::cast($row['t3ver_state']);
7987  $tsfe->sys_page->versionOL('pages', $row);
7988  if ($row['doktype'] == PageRepository::DOKTYPE_RECYCLER
7989  || $row['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION
7990  || $versionState->indicatesPlaceholder()
7991  ) {
7992  // Doing this after the overlay to make sure changes
7993  // in the overlay are respected.
7994  // However, we do not process pages below of and
7995  // including of type recycler and BE user section
7996  continue;
7997  }
7998  // Find mount point if any:
7999  $next_id = $row['uid'];
8000  $mount_info = $tsfe->sys_page->getMountPointInfo($next_id, $row);
8001  // Overlay mode:
8002  if (is_array($mount_info) && $mount_info['overlay']) {
8003  $next_id = $mount_info['mount_pid'];
8004  $row = $db->exec_SELECTgetSingleRow(
8005  $allFields,
8006  'pages',
8007  'uid = ' . (int)$next_id . ' AND deleted = 0 ' . $moreWhereClauses,
8008  '',
8009  'sorting'
8010  );
8011  $tsfe->sys_page->versionOL('pages', $row);
8012  if ($row['doktype'] == PageRepository::DOKTYPE_RECYCLER
8013  || $row['doktype'] == PageRepository::DOKTYPE_BE_USER_SECTION
8014  || $versionState->indicatesPlaceholder()
8015  ) {
8016  // Doing this after the overlay to make sure
8017  // changes in the overlay are respected.
8018  // see above
8019  continue;
8020  }
8021  }
8022  // Add record:
8023  if ($dontCheckEnableFields || $tsfe->checkPagerecordForIncludeSection($row)) {
8024  // Add ID to list:
8025  if ($begin <= 0) {
8026  if ($dontCheckEnableFields || $tsfe->checkEnableFields($row)) {
8027  $theList[] = $next_id;
8028  }
8029  }
8030  // Next level:
8031  if ($depth > 1 && !$row['php_tree_stop']) {
8032  // Normal mode:
8033  if (is_array($mount_info) && !$mount_info['overlay']) {
8034  $next_id = $mount_info['mount_pid'];
8035  }
8036  // Call recursively, if the id is not in prevID_array:
8037  if (!in_array($next_id, $prevId_array)) {
8038  $theList = array_merge(
8039  GeneralUtility::intExplode(
8040  ',',
8041  $this->getTreeList($next_id, $depth - 1, $begin - 1,
8042  $dontCheckEnableFields, $addSelectFields, $moreWhereClauses,
8043  $prevId_array, $recursionLevel + 1),
8044  true
8045  ),
8046  $theList
8047  );
8048  }
8049  }
8050  }
8051  }
8052  }
8053  }
8054  // If first run, check if the ID should be returned:
8055  if (!$recursionLevel) {
8056  if ($addId) {
8057  if ($begin > 0) {
8058  $theList[] = 0;
8059  } else {
8060  $theList[] = $addId;
8061  }
8062  }
8063  $db->exec_INSERTquery('cache_treelist', array(
8064  'md5hash' => $requestHash,
8065  'pid' => $id,
8066  'treelist' => implode(',', $theList),
8067  'tstamp' => $GLOBALS['EXEC_TIME']
8068  ));
8069  }
8070 
8071  return implode(',', $theList);
8072  }
8073 
8088  public function exec_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
8089  {
8090  return $this->getDatabaseConnection()->exec_SELECTquery(
8091  $select,
8092  $local_table . ',' . $mm_table . ($foreign_table ? ',' . $foreign_table : ''),
8093  $local_table . '.uid=' . $mm_table . '.uid_local' . ($foreign_table ? ' AND ' . $foreign_table . '.uid=' . $mm_table . '.uid_foreign' : '') . $whereClause,
8094  $groupBy,
8095  $orderBy,
8096  $limit
8097  );
8098  }
8099 
8115  public function exec_mm_query_uidList($select, $local_table_uidlist, $mm_table, $foreign_table = '', $whereClause = '', $groupBy = '', $orderBy = '', $limit = '')
8116  {
8117  return $this->getDatabaseConnection()->exec_SELECTquery(
8118  $select,
8119  $mm_table . ($foreign_table ? ',' . $foreign_table : ''),
8120  $mm_table . '.uid_local IN (' . $local_table_uidlist . ')' . ($foreign_table ? ' AND ' . $foreign_table . '.uid=' . $mm_table . '.uid_foreign' : '') . $whereClause,
8121  $groupBy,
8122  $orderBy,
8123  $limit
8124  );
8125  }
8126 
8136  public function searchWhere($sw, $searchFieldList, $searchTable = '')
8137  {
8138  $prefixTableName = $searchTable ? $searchTable . '.' : '';
8139  $where = '';
8140  if ($sw) {
8141  $searchFields = explode(',', $searchFieldList);
8142  $kw = preg_split('/[ ,]/', $sw);
8143  $db = $this->getDatabaseConnection();
8144  foreach ($kw as $val) {
8145  $val = trim($val);
8146  $where_p = array();
8147  if (strlen($val) >= 2) {
8148  $val = $db->escapeStrForLike($db->quoteStr($val, $searchTable), $searchTable);
8149  foreach ($searchFields as $field) {
8150  $where_p[] = $prefixTableName . $field . ' LIKE \'%' . $val . '%\'';
8151  }
8152  }
8153  if (!empty($where_p)) {
8154  $where .= ' AND (' . implode(' OR ', $where_p) . ')';
8155  }
8156  }
8157  }
8158  return $where;
8159  }
8160 
8170  public function exec_getQuery($table, $conf)
8171  {
8172  $queryParts = $this->getQuery($table, $conf, true);
8173  return $this->getDatabaseConnection()->exec_SELECT_queryArray($queryParts);
8174  }
8175 
8184  public function getRecords($tableName, array $queryConfiguration)
8185  {
8186  $records = [];
8187 
8188  $res = $this->exec_getQuery($tableName, $queryConfiguration);
8189 
8190  $db = $this->getDatabaseConnection();
8191  if ($error = $db->sql_error()) {
8192  $this->getTimeTracker()->setTSlogMessage($error, 3);
8193  } else {
8194  $tsfe = $this->getTypoScriptFrontendController();
8195  while (($row = $db->sql_fetch_assoc($res)) !== false) {
8196 
8197  // Versioning preview:
8198  $tsfe->sys_page->versionOL($tableName, $row, true);
8199 
8200  // Language overlay:
8201  if (is_array($row) && $tsfe->sys_language_contentOL) {
8202  if ($tableName === 'pages') {
8203  $row = $tsfe->sys_page->getPageOverlay($row);
8204  } else {
8205  $row = $tsfe->sys_page->getRecordOverlay(
8206  $tableName,
8207  $row,
8208  $tsfe->sys_language_content,
8209  $tsfe->sys_language_contentOL
8210  );
8211  }
8212  }
8213 
8214  // Might be unset in the sys_language_contentOL
8215  if (is_array($row)) {
8216  $records[] = $row;
8217  }
8218  }
8219  $db->sql_free_result($res);
8220  }
8221 
8222  return $records;
8223  }
8224 
8236  public function getQuery($table, $conf, $returnQueryArray = false)
8237  {
8238  // Resolve stdWrap in these properties first
8239  $properties = array(
8240  'pidInList',
8241  'uidInList',
8242  'languageField',
8243  'selectFields',
8244  'max',
8245  'begin',
8246  'groupBy',
8247  'orderBy',
8248  'join',
8249  'leftjoin',
8250  'rightjoin',
8251  'recursive',
8252  'where'
8253  );
8254  foreach ($properties as $property) {
8255  $conf[$property] = trim(isset($conf[$property . '.'])
8256  ? $this->stdWrap($conf[$property], $conf[$property . '.'])
8257  : $conf[$property]
8258  );
8259  if ($conf[$property] === '') {
8260  unset($conf[$property]);
8261  }
8262  if (isset($conf[$property . '.'])) {
8263  // stdWrapping already done, so remove the sub-array
8264  unset($conf[$property . '.']);
8265  }
8266  }
8267  // Handle PDO-style named parameter markers first
8268  $queryMarkers = $this->getQueryMarkers($table, $conf);
8269  // Replace the markers in the non-stdWrap properties
8270  foreach ($queryMarkers as $marker => $markerValue) {
8271  $properties = array(
8272  'uidInList',
8273  'selectFields',
8274  'where',
8275  'max',
8276  'begin',
8277  'groupBy',
8278  'orderBy',
8279  'join',
8280  'leftjoin',
8281  'rightjoin'
8282  );
8283  foreach ($properties as $property) {
8284  if ($conf[$property]) {
8285  $conf[$property] = str_replace('###' . $marker . '###', $markerValue, $conf[$property]);
8286  }
8287  }
8288  }
8289  // Construct WHERE clause:
8290  // Handle recursive function for the pidInList
8291  if (isset($conf['recursive'])) {
8292  $conf['recursive'] = (int)$conf['recursive'];
8293  if ($conf['recursive'] > 0) {
8294  $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true);
8295  array_walk($pidList, function (&$storagePid) {
8296  if ($storagePid === 'this') {
8297  $storagePid = $this->getTypoScriptFrontendController()->id;
8298  }
8299  if ($storagePid > 0) {
8300  $storagePid = -$storagePid;
8301  }
8302  });
8303  $expandedPidList = array();
8304  foreach ($pidList as $value) {
8305  // Implementation of getTreeList allows to pass the id negative to include
8306  // it into the result otherwise only childpages are returned
8307  $expandedPidList = array_merge(
8308  GeneralUtility::intExplode(',', $this->getTreeList($value, $conf['recursive'])),
8309  $expandedPidList
8310  );
8311  }
8312  $conf['pidInList'] = implode(',', $expandedPidList);
8313  }
8314  }
8315  if ((string)$conf['pidInList'] === '') {
8316  $conf['pidInList'] = 'this';
8317  }
8318  $queryParts = $this->getWhere($table, $conf, true);
8319  // Fields:
8320  if ($conf['selectFields']) {
8321  $queryParts['SELECT'] = $this->sanitizeSelectPart($conf['selectFields'], $table);
8322  } else {
8323  $queryParts['SELECT'] = '*';
8324  }
8325  // Setting LIMIT:
8326  $db = $this->getDatabaseConnection();
8327  $error = 0;
8328  if ($conf['max'] || $conf['begin']) {
8329  // Finding the total number of records, if used:
8330  if (strstr(strtolower($conf['begin'] . $conf['max']), 'total')) {
8331  $res = $db->exec_SELECTquery('count(*)', $table, $queryParts['WHERE'], $queryParts['GROUPBY']);
8332  if ($error = $db->sql_error()) {
8333  $this->getTimeTracker()->setTSlogMessage($error);
8334  } else {
8335  $row = $db->sql_fetch_row($res);
8336  $conf['max'] = str_ireplace('total', $row[0], $conf['max']);
8337  $conf['begin'] = str_ireplace('total', $row[0], $conf['begin']);
8338  }
8339  $db->sql_free_result($res);
8340  }
8341  if (!$error) {
8342  $conf['begin'] = MathUtility::forceIntegerInRange(ceil($this->calc($conf['begin'])), 0);
8343  $conf['max'] = MathUtility::forceIntegerInRange(ceil($this->calc($conf['max'])), 0);
8344  if ($conf['begin'] && !$conf['max']) {
8345  $conf['max'] = 100000;
8346  }
8347  if ($conf['begin'] && $conf['max']) {
8348  $queryParts['LIMIT'] = $conf['begin'] . ',' . $conf['max'];
8349  } elseif (!$conf['begin'] && $conf['max']) {
8350  $queryParts['LIMIT'] = $conf['max'];
8351  }
8352  }
8353  }
8354  if (!$error) {
8355  // Setting up tablejoins:
8356  $joinPart = '';
8357  if ($conf['join']) {
8358  $joinPart = 'JOIN ' . $conf['join'];
8359  } elseif ($conf['leftjoin']) {
8360  $joinPart = 'LEFT OUTER JOIN ' . $conf['leftjoin'];
8361  } elseif ($conf['rightjoin']) {
8362  $joinPart = 'RIGHT OUTER JOIN ' . $conf['rightjoin'];
8363  }
8364  // Compile and return query:
8365  $queryParts['FROM'] = trim($table . ' ' . $joinPart);
8366  // Replace the markers in the queryParts to handle stdWrap
8367  // enabled properties
8368  foreach ($queryMarkers as $marker => $markerValue) {
8369  foreach ($queryParts as $queryPartKey => &$queryPartValue) {
8370  $queryPartValue = str_replace('###' . $marker . '###', $markerValue, $queryPartValue);
8371  }
8372  unset($queryPartValue);
8373  }
8374  $query = $db->SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
8375  return $returnQueryArray ? $queryParts : $query;
8376  }
8377  return '';
8378  }
8379 
8390  public function getWhere($table, $conf, $returnQueryArray = false)
8391  {
8392  // Init:
8393  $query = '';
8394  $pid_uid_flag = 0;
8395  $enableFieldsIgnore = array();
8396  $queryParts = array(
8397  'SELECT' => '',
8398  'FROM' => '',
8399  'WHERE' => '',
8400  'GROUPBY' => '',
8401  'ORDERBY' => '',
8402  'LIMIT' => ''
8403  );
8404  $tsfe = $this->getTypoScriptFrontendController();
8405  $considerMovePlaceholders = (
8406  $tsfe->sys_page->versioningPreview && $table !== 'pages'
8407  && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
8408  );
8409  if (trim($conf['uidInList'])) {
8410  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['uidInList']));
8411  if (count($listArr) === 1) {
8412  $comparison = '=' . (int)$listArr[0];
8413  } else {
8414  $comparison = ' IN (' . implode(',', $this->getDatabaseConnection()->cleanIntArray($listArr)) . ')';
8415  }
8416  // If move placeholder shall be considered, select via t3ver_move_id
8417  if ($considerMovePlaceholders) {
8418  $movePlaceholderComparison = $table . '.t3ver_state=' . VersionState::cast(VersionState::MOVE_PLACEHOLDER) . ' AND ' . $table . '.t3ver_move_id' . $comparison;
8419  $query .= ' AND (' . $table . '.uid' . $comparison . ' OR ' . $movePlaceholderComparison . ')';
8420  } else {
8421  $query .= ' AND ' . $table . '.uid' . $comparison;
8422  }
8423  $pid_uid_flag++;
8424  }
8425  // Static_* tables are allowed to be fetched from root page
8426  if (substr($table, 0, 7) == 'static_') {
8427  $pid_uid_flag++;
8428  }
8429  if (trim($conf['pidInList'])) {
8430  $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $conf['pidInList']));
8431  // Removes all pages which are not visible for the user!
8432  $listArr = $this->checkPidArray($listArr);
8433  if (GeneralUtility::inList($conf['pidInList'], 'root')) {
8434  $listArr[] = 0;
8435  }
8436  if (GeneralUtility::inList($conf['pidInList'], '-1')) {
8437  $listArr[] = -1;
8438  $enableFieldsIgnore['pid'] = true;
8439  }
8440  if (!empty($listArr)) {
8441  $query .= ' AND ' . $table . '.pid IN (' . implode(',', array_map('intval', $listArr)) . ')';
8442  $pid_uid_flag++;
8443  } else {
8444  // If not uid and not pid then uid is set to 0 - which results in nothing!!
8445  $pid_uid_flag = 0;
8446  }
8447  }
8448  // If not uid and not pid then uid is set to 0 - which results in nothing!!
8449  if (!$pid_uid_flag) {
8450  $query .= ' AND ' . $table . '.uid=0';
8451  }
8452  $where = isset($conf['where.']) ? trim($this->stdWrap($conf['where'], $conf['where.'])) : trim($conf['where']);
8453  if ($where) {
8454  $query .= ' AND ' . $where;
8455  }
8456 
8457  // Check if the table is translatable, and set the language field by default from the TCA information
8458  $languageField = '';
8459  if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
8460  if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
8461  $languageField = $conf['languageField'];
8462  } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
8463  $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField'];
8464  }
8465  }
8466 
8467  if (!empty($languageField)) {
8468  // The sys_language record UID of the content of the page
8469  $sys_language_content = (int)$tsfe->sys_language_content;
8470 
8471  if ($tsfe->sys_language_contentOL && !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'])) {
8472  // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
8473  // OVERLAY the records with localized versions!
8474  $languageQuery = $languageField . ' IN (0,-1)';
8475  // Use this option to include records that don't have a default translation
8476  // (originalpointerfield is 0 and the language field contains the requested language)
8477  $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ?
8478  $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) :
8479  $conf['includeRecordsWithoutDefaultTranslation'];
8480  if (!empty(trim($includeRecordsWithoutDefaultTranslation))) {
8481  $languageQuery .= ' OR (' . $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] . ' = 0 AND ' .
8482  $languageField . ' = ' . $sys_language_content . ')';
8483  }
8484  } else {
8485  $languageQuery = $languageField . ' = ' . $sys_language_content;
8486  }
8487  $query .= ' AND (' . $languageQuery . ')';
8488  }
8489  $andWhere = isset($conf['andWhere.']) ? trim($this->stdWrap($conf['andWhere'], $conf['andWhere.'])) : trim($conf['andWhere']);
8490  if ($andWhere) {
8491  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 . '".');
8492  $query .= ' AND ' . $andWhere;
8493  }
8494  // Enablefields
8495  if ($table == 'pages') {
8496  $query .= ' ' . $tsfe->sys_page->where_hid_del . $tsfe->sys_page->where_groupAccess;
8497  } else {
8498  $query .= $this->enableFields($table, false, $enableFieldsIgnore);
8499  }
8500  // MAKE WHERE:
8501  if ($query) {
8502  // Stripping of " AND"...
8503  $queryParts['WHERE'] = trim(substr($query, 4));
8504  $query = 'WHERE ' . $queryParts['WHERE'];
8505  }
8506  // GROUP BY
8507  if (trim($conf['groupBy'])) {
8508  $queryParts['GROUPBY'] = isset($conf['groupBy.']) ? trim($this->stdWrap($conf['groupBy'], $conf['groupBy.'])) : trim($conf['groupBy']);
8509  $query .= ' GROUP BY ' . $queryParts['GROUPBY'];
8510  }
8511  // ORDER BY
8512  if (trim($conf['orderBy'])) {
8513  $queryParts['ORDERBY'] = isset($conf['orderBy.']) ? trim($this->stdWrap($conf['orderBy'], $conf['orderBy.'])) : trim($conf['orderBy']);
8514  $query .= ' ORDER BY ' . $queryParts['ORDERBY'];
8515  }
8516  // Return result:
8517  return $returnQueryArray ? $queryParts : $query;
8518  }
8519 
8532  protected function sanitizeSelectPart($selectPart, $table)
8533  {
8534  // Pattern matching parts
8535  $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)';
8536  $matchEnd = '(\\s*,|\\s*$)/';
8537  $necessaryFields = array('uid', 'pid');
8538  $wsFields = array('t3ver_state');
8539  if (isset($GLOBALS['TCA'][$table]) && !preg_match(($matchStart . '\\*' . $matchEnd), $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)/i', $selectPart)) {
8540  foreach ($necessaryFields as $field) {
8541  $match = $matchStart . $field . $matchEnd;
8542  if (!preg_match($match, $selectPart)) {
8543  $selectPart .= ', ' . $table . '.' . $field . ' as ' . $field;
8544  }
8545  }
8546  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
8547  foreach ($wsFields as $field) {
8548  $match = $matchStart . $field . $matchEnd;
8549  if (!preg_match($match, $selectPart)) {
8550  $selectPart .= ', ' . $table . '.' . $field . ' as ' . $field;
8551  }
8552  }
8553  }
8554  }
8555  return $selectPart;
8556  }
8557 
8566  public function checkPidArray($listArr)
8567  {
8568  if (!is_array($listArr) || empty($listArr)) {
8569  return array();
8570  }
8571  $outArr = array();
8572  $db = $this->getDatabaseConnection();
8573  $res = $db->exec_SELECTquery('uid', 'pages', 'uid IN (' . implode(',', $listArr) . ')' . $this->enableFields('pages') . ' AND doktype NOT IN (' . $this->checkPid_badDoktypeList . ')');
8574  if ($error = $db->sql_error()) {
8575  $this->getTimeTracker()->setTSlogMessage($error . ': ' . $db->debug_lastBuiltQuery, 3);
8576  } else {
8577  while ($row = $db->sql_fetch_assoc($res)) {
8578  $outArr[] = $row['uid'];
8579  }
8580  }
8581  $db->sql_free_result($res);
8582  return $outArr;
8583  }
8584 
8593  public function checkPid($uid)
8594  {
8595  $uid = (int)$uid;
8596  if (!isset($this->checkPid_cache[$uid])) {
8597  $count = $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'pages', 'uid=' . $uid . $this->enableFields('pages') . ' AND doktype NOT IN (' . $this->checkPid_badDoktypeList . ')');
8598  $this->checkPid_cache[$uid] = (bool)$count;
8599  }
8600  return $this->checkPid_cache[$uid];
8601  }
8602 
8613  public function getQueryMarkers($table, $conf)
8614  {
8615  if (!is_array($conf['markers.'])) {
8616  return array();
8617  }
8618  // Parse markers and prepare their values
8619  $db = $this->getDatabaseConnection();
8620  $markerValues = array();
8621  foreach ($conf['markers.'] as $dottedMarker => $dummy) {
8622  $marker = rtrim($dottedMarker, '.');
8623  if ($dottedMarker != $marker . '.') {
8624  continue;
8625  }
8626  // Parse definition
8627  $tempValue = isset($conf['markers.'][$dottedMarker])
8628  ? $this->stdWrap($conf['markers.'][$dottedMarker]['value'], $conf['markers.'][$dottedMarker])
8629  : $conf['markers.'][$dottedMarker]['value'];
8630  // Quote/escape if needed
8631  if (is_numeric($tempValue)) {
8632  if ((int)$tempValue == $tempValue) {
8633  // Handle integer
8634  $markerValues[$marker] = (int)$tempValue;
8635  } else {
8636  // Handle float
8637  $markerValues[$marker] = floatval($tempValue);
8638  }
8639  } elseif (is_null($tempValue)) {
8640  // It represents NULL
8641  $markerValues[$marker] = 'NULL';
8642  } elseif ($conf['markers.'][$dottedMarker]['commaSeparatedList'] == 1) {
8643  // See if it is really a comma separated list of values
8644  $explodeValues = GeneralUtility::trimExplode(',', $tempValue);
8645  if (count($explodeValues) > 1) {
8646  // Handle each element of list separately
8647  $tempArray = array();
8648  foreach ($explodeValues as $listValue) {
8649  if (is_numeric($listValue)) {
8650  if ((int)$listValue == $listValue) {
8651  $tempArray[] = (int)$listValue;
8652  } else {
8653  $tempArray[] = floatval($listValue);
8654  }
8655  } else {
8656  // If quoted, remove quotes before
8657  // escaping.
8658  if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) {
8659  $listValue = $matches[1];
8660  } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) {
8661  $listValue = $matches[1];
8662  }
8663  $tempArray[] = $db->fullQuoteStr($listValue, $table);
8664  }
8665  }
8666  $markerValues[$marker] = implode(',', $tempArray);
8667  } else {
8668  // Handle remaining values as string
8669  $markerValues[$marker] = $db->fullQuoteStr($tempValue, $table);
8670  }
8671  } else {
8672  // Handle remaining values as string
8673  $markerValues[$marker] = $db->fullQuoteStr($tempValue, $table);
8674  }
8675  }
8676  return $markerValues;
8677  }
8678 
8679  /***********************************************
8680  *
8681  * Frontend editing functions
8682  *
8683  ***********************************************/
8695  public function editPanel($content, $conf, $currentRecord = '', $dataArr = array())
8696  {
8697  if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
8698  if (!$currentRecord) {
8699  $currentRecord = $this->currentRecord;
8700  }
8701  if (empty($dataArr)) {
8702  $dataArr = $this->data;
8703  }
8704  // Delegate rendering of the edit panel to the frontend edit
8705  $content = $this->getFrontendBackendUser()->frontendEdit->displayEditPanel($content, $conf, $currentRecord, $dataArr);
8706  }
8707  return $content;
8708  }
8709 
8722  public function editIcons($content, $params, array $conf = array(), $currentRecord = '', $dataArr = array(), $addUrlParamStr = '')
8723  {
8724  if ($this->getTypoScriptFrontendController()->beUserLogin && $this->getFrontendBackendUser()->frontendEdit instanceof FrontendEditingController) {
8725  if (!$currentRecord) {
8726  $currentRecord = $this->currentRecord;
8727  }
8728  if (empty($dataArr)) {
8729  $dataArr = $this->data;
8730  }
8731  // Delegate rendering of the edit panel to frontend edit class.
8732  $content = $this->getFrontendBackendUser()->frontendEdit->displayEditIcons($content, $params, $conf, $currentRecord, $dataArr, $addUrlParamStr);
8733  }
8734  return $content;
8735  }
8736 
8746  public function isDisabled($table, $row)
8747  {
8748  $tsfe = $this->getTypoScriptFrontendController();
8749  $enablecolumns = $GLOBALS['TCA'][$table]['ctrl']['enablecolumns'];
8750  return $enablecolumns['disabled'] && $row[$enablecolumns['disabled']]
8751  || $enablecolumns['fe_group'] && $tsfe->simUserGroup && $row[$enablecolumns['fe_group']] == $tsfe->simUserGroup
8752  || $enablecolumns['starttime'] && $row[$enablecolumns['starttime']] > $GLOBALS['EXEC_TIME']
8753  || $enablecolumns['endtime'] && $row[$enablecolumns['endtime']] && $row[$enablecolumns['endtime']] < $GLOBALS['EXEC_TIME'];
8754  }
8755 
8761  protected function getResourceFactory()
8762  {
8763  return ResourceFactory::getInstance();
8764  }
8765 
8773  protected function getEnvironmentVariable($key)
8774  {
8775  return GeneralUtility::getIndpEnv($key);
8776  }
8777 
8785  protected function getFromCache(array $configuration)
8786  {
8787  $content = false;
8788 
8789  $cacheKey = $this->calculateCacheKey($configuration);
8790  if (!empty($cacheKey)) {
8792  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)
8793  ->getCache('cache_hash');
8794  $content = $cacheFrontend->get($cacheKey);
8795  }
8796  return $content;
8797  }
8798 
8805  protected function calculateCacheLifetime(array $configuration)
8806  {
8807  $lifetimeConfiguration = isset($configuration['lifetime'])
8808  ? $configuration['lifetime']
8809  : '';
8810  $lifetimeConfiguration = isset($configuration['lifetime.'])
8811  ? $this->stdWrap($lifetimeConfiguration, $configuration['lifetime.'])
8812  : $lifetimeConfiguration;
8813 
8814  $lifetime = null; // default lifetime
8815  if (strtolower($lifetimeConfiguration) === 'unlimited') {
8816  $lifetime = 0; // unlimited
8817  } elseif ($lifetimeConfiguration > 0) {
8818  $lifetime = (int)$lifetimeConfiguration; // lifetime in seconds
8819  }
8820  return $lifetime;
8821  }
8822 
8829  protected function calculateCacheTags(array $configuration)
8830  {
8831  $tags = isset($configuration['tags']) ? $configuration['tags'] : '';
8832  $tags = isset($configuration['tags.'])
8833  ? $this->stdWrap($tags, $configuration['tags.'])
8834  : $tags;
8835  return empty($tags) ? [] : GeneralUtility::trimExplode(',', $tags);
8836  }
8837 
8844  protected function calculateCacheKey(array $configuration)
8845  {
8846  $key = isset($configuration['key']) ? $configuration['key'] : '';
8847  return isset($configuration['key.'])
8848  ? $this->stdWrap($key, $configuration['key.'])
8849  : $key;
8850  }
8851 
8857  protected function getFrontendBackendUser()
8858  {
8859  return $GLOBALS['BE_USER'];
8860  }
8861 
8867  protected function getDatabaseConnection()
8868  {
8869  return $GLOBALS['TYPO3_DB'];
8870  }
8871 
8875  protected function getTimeTracker()
8876  {
8877  return $GLOBALS['TT'];
8878  }
8879 
8883  protected function getTypoScriptFrontendController()
8884  {
8885  return $this->typoScriptFrontendController ?: $GLOBALS['TSFE'];
8886  }
8887 }