‪TYPO3CMS  ‪main
ContentObjectRenderer.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Doctrine\DBAL\Exception as DBALException;
19 use Doctrine\DBAL\Result;
20 use Psr\Container\ContainerInterface;
21 use Psr\EventDispatcher\EventDispatcherInterface;
22 use Psr\Http\Message\ServerRequestInterface;
23 use Psr\Log\LoggerAwareInterface;
24 use Psr\Log\LoggerAwareTrait;
25 use Psr\Log\LogLevel;
44 use TYPO3\CMS\Core\Imaging\ImageResource;
88 use TYPO3\CMS\Frontend\Typolink\LinkResult;
92 use TYPO3\HtmlSanitizer\Builder\BuilderInterface;
93 
101 class ‪ContentObjectRenderer implements LoggerAwareInterface
102 {
103  use LoggerAwareTrait;
105 
109  protected ‪$container;
110 
117  public array ‪$stdWrapOrder = [
118  BeforeStdWrapFunctionsInitializedEvent::class => 'event',
119  'cacheRead' => 'hook', // this is a placeholder for checking if the content is available in cache
120  'setContentToCurrent' => 'boolean',
121  'setContentToCurrent.' => 'array',
122  'addPageCacheTags' => 'string',
123  'addPageCacheTags.' => 'array',
124  'setCurrent' => 'string',
125  'setCurrent.' => 'array',
126  'lang.' => 'array',
127  'data' => 'getText',
128  'data.' => 'array',
129  'field' => 'fieldName',
130  'field.' => 'array',
131  'current' => 'boolean',
132  'current.' => 'array',
133  'cObject' => 'cObject',
134  'cObject.' => 'array',
135  'numRows.' => 'array',
136  'preUserFunc' => 'functionName',
137  AfterStdWrapFunctionsInitializedEvent::class => 'event',
138  'override' => 'string',
139  'override.' => 'array',
140  'preIfEmptyListNum' => 'listNum',
141  'preIfEmptyListNum.' => 'array',
142  'ifNull' => 'string',
143  'ifNull.' => 'array',
144  'ifEmpty' => 'string',
145  'ifEmpty.' => 'array',
146  'ifBlank' => 'string',
147  'ifBlank.' => 'array',
148  'listNum' => 'listNum',
149  'listNum.' => 'array',
150  'trim' => 'boolean',
151  'trim.' => 'array',
152  'strPad.' => 'array',
153  'stdWrap' => 'stdWrap',
154  'stdWrap.' => 'array',
155  BeforeStdWrapFunctionsExecutedEvent::class => 'event',
156  'required' => 'boolean',
157  'required.' => 'array',
158  'if.' => 'array',
159  'fieldRequired' => 'fieldName',
160  'fieldRequired.' => 'array',
161  'csConv' => 'string',
162  'csConv.' => 'array',
163  'parseFunc' => 'objectpath',
164  'parseFunc.' => 'array',
165  'HTMLparser' => 'boolean',
166  'HTMLparser.' => 'array',
167  'split.' => 'array',
168  'replacement.' => 'array',
169  'prioriCalc' => 'boolean',
170  'prioriCalc.' => 'array',
171  'char' => 'integer',
172  'char.' => 'array',
173  'intval' => 'boolean',
174  'intval.' => 'array',
175  'hash' => 'string',
176  'hash.' => 'array',
177  'round' => 'boolean',
178  'round.' => 'array',
179  'numberFormat.' => 'array',
180  'expandList' => 'boolean',
181  'expandList.' => 'array',
182  'date' => 'dateconf',
183  'date.' => 'array',
184  'strtotime' => 'strtotimeconf',
185  'strtotime.' => 'array',
186  'strftime' => 'strftimeconf',
187  'strftime.' => 'array',
188  'formattedDate' => 'formattedDateconf',
189  'formattedDate.' => 'array',
190  'age' => 'boolean',
191  'age.' => 'array',
192  'case' => 'case',
193  'case.' => 'array',
194  'bytes' => 'boolean',
195  'bytes.' => 'array',
196  'substring' => 'parameters',
197  'substring.' => 'array',
198  'cropHTML' => 'crop',
199  'cropHTML.' => 'array',
200  'stripHtml' => 'boolean',
201  'stripHtml.' => 'array',
202  'crop' => 'crop',
203  'crop.' => 'array',
204  'rawUrlEncode' => 'boolean',
205  'rawUrlEncode.' => 'array',
206  'htmlSpecialChars' => 'boolean',
207  'htmlSpecialChars.' => 'array',
208  'encodeForJavaScriptValue' => 'boolean',
209  'encodeForJavaScriptValue.' => 'array',
210  'doubleBrTag' => 'string',
211  'doubleBrTag.' => 'array',
212  'br' => 'boolean',
213  'br.' => 'array',
214  'brTag' => 'string',
215  'brTag.' => 'array',
216  'encapsLines.' => 'array',
217  'keywords' => 'boolean',
218  'keywords.' => 'array',
219  'innerWrap' => 'wrap',
220  'innerWrap.' => 'array',
221  'innerWrap2' => 'wrap',
222  'innerWrap2.' => 'array',
223  'preCObject' => 'cObject',
224  'preCObject.' => 'array',
225  'postCObject' => 'cObject',
226  'postCObject.' => 'array',
227  'wrapAlign' => 'align',
228  'wrapAlign.' => 'array',
229  'typolink.' => 'array',
230  'wrap' => 'wrap',
231  'wrap.' => 'array',
232  'noTrimWrap' => 'wrap',
233  'noTrimWrap.' => 'array',
234  'wrap2' => 'wrap',
235  'wrap2.' => 'array',
236  'dataWrap' => 'dataWrap',
237  'dataWrap.' => 'array',
238  'prepend' => 'cObject',
239  'prepend.' => 'array',
240  'append' => 'cObject',
241  'append.' => 'array',
242  'wrap3' => 'wrap',
243  'wrap3.' => 'array',
244  'orderedStdWrap' => 'stdWrap',
245  'orderedStdWrap.' => 'array',
246  'outerWrap' => 'wrap',
247  'outerWrap.' => 'array',
248  'insertData' => 'boolean',
249  'insertData.' => 'array',
250  'postUserFunc' => 'functionName',
251  'postUserFuncInt' => 'functionName',
252  'prefixComment' => 'string',
253  'prefixComment.' => 'array',
254  'htmlSanitize' => 'boolean',
255  'htmlSanitize.' => 'array',
256  'cacheStore' => 'hook', // this is a placeholder for storing the content in cache
257  AfterStdWrapFunctionsExecutedEvent::class => 'event',
258  'debug' => 'boolean',
259  'debug.' => 'array',
260  'debugFunc' => 'boolean',
261  'debugFunc.' => 'array',
262  'debugData' => 'boolean',
263  'debugData.' => 'array',
264  ];
265 
275  public ‪$data = [];
276 
280  protected ‪$table = '';
281 
287  public ‪$parameters = [];
288 
292  public ‪$currentValKey = 'currentValue_kidjls9dksoje';
293 
300  public ‪$currentRecord = '';
301 
307  public ‪$currentRecordNumber = 0;
308 
314  public ‪$parentRecordNumber = 0;
315 
321  public ‪$parentRecord = [];
322 
328 
330 
334  protected ‪$currentFile;
335 
340  public ‪$doConvertToUserIntObject = false;
341 
348  protected ‪$userObjectType = false;
349 
353  protected ‪$stopRendering = [];
354 
358  protected ‪$stdWrapRecursionLevel = 0;
359 
364 
368  private ?ServerRequestInterface ‪$request = null;
369 
375  public const ‪OBJECTTYPE_USER_INT = 1;
381  public const ‪OBJECTTYPE_USER = 2;
382 
383  public function ‪__construct(‪TypoScriptFrontendController ‪$typoScriptFrontendController = null, ContainerInterface ‪$container = null)
384  {
385  $this->typoScriptFrontendController = ‪$typoScriptFrontendController;
386  $this->container = ‪$container;
387  }
388 
389  public function ‪setRequest(ServerRequestInterface ‪$request): void
390  {
391  $this->request = ‪$request;
392  }
393 
401  public function ‪__sleep()
402  {
403  $vars = get_object_vars($this);
404  unset($vars['typoScriptFrontendController'], $vars['logger'], $vars['container'], $vars['request']);
405  if ($this->currentFile instanceof ‪FileReference) {
406  $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
407  } elseif ($this->currentFile instanceof ‪File) {
408  $this->currentFile = 'File:' . $this->currentFile->getIdentifier();
409  } else {
410  unset($vars['currentFile']);
411  }
412  return array_keys($vars);
413  }
414 
420  public function ‪__wakeup()
421  {
422  if (isset(‪$GLOBALS['TSFE'])) {
423  $this->typoScriptFrontendController = ‪$GLOBALS['TSFE'];
424  }
425  if (is_string($this->currentFile)) {
426  [$objectType, ‪$identifier] = explode(':', $this->currentFile, 2);
427  try {
428  if ($objectType === 'File') {
429  $this->currentFile = GeneralUtility::makeInstance(ResourceFactory::class)->retrieveFileOrFolderObject(‪$identifier);
430  } elseif ($objectType === 'FileReference') {
431  $this->currentFile = GeneralUtility::makeInstance(ResourceFactory::class)->getFileReferenceObject((int)‪$identifier);
432  }
433  } catch (ResourceDoesNotExistException $e) {
434  $this->currentFile = null;
435  }
436  }
437  $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
438  $this->container = GeneralUtility::getContainer();
439 
440  // We do not derive $this->request from globals here. The request is expected to be injected
441  // using setRequest(), a fallback to $GLOBALS['TYPO3_REQUEST'] is available in getRequest() for BC.
442  }
443 
452  public function ‪start(‪$data, ‪$table = '')
453  {
454  $this->data = ‪$data;
455  $this->table = ‪$table;
456  $this->currentRecord = ‪$table !== ''
457  ? ‪$table . ':' . ($this->data['uid'] ?? '')
458  : '';
459  $this->parameters = [];
460 
461  GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
463  );
464  }
465 
471  public function ‪getCurrentTable()
472  {
473  return ‪$this->table;
474  }
475 
484  public function ‪setParent(‪$data, ‪$currentRecord)
485  {
486  $this->parentRecord = [
487  'data' => ‪$data,
488  'currentRecord' => ‪$currentRecord,
489  ];
490  }
491 
492  /***********************************************
493  *
494  * CONTENT_OBJ:
495  *
496  ***********************************************/
505  public function ‪getCurrentVal()
506  {
507  return $this->data[‪$this->currentValKey] ?? null;
508  }
509 
516  public function ‪setCurrentVal($value)
517  {
518  $this->data[‪$this->currentValKey] = $value;
519  }
520 
530  public function ‪cObjGet($setup, $addKey = '')
531  {
532  if (!is_array($setup)) {
533  return '';
534  }
535  return implode('', $this->‪cObjGetSeparated($setup, $addKey));
536  }
537 
544  public function ‪cObjGetSeparated(?array $setup, string $addKey = ''): array
545  {
546  if ($setup === null || $setup === []) {
547  return [];
548  }
549  $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($setup);
550  $contentObjects = [];
551  foreach ($sKeyArray as $theKey) {
552  $theValue = $setup[$theKey];
553  if ((int)$theKey && !str_contains($theKey, '.')) {
554  $conf = $setup[$theKey . '.'] ?? [];
555  $contentObjects[] = $this->‪cObjGetSingle($theValue, $conf, $addKey . $theKey);
556  }
557  }
558  return $contentObjects;
559  }
560 
570  public function ‪cObjGetSingle(string $name, $conf, $TSkey = '__')
571  {
572  $timeTracker = $this->‪getTimeTracker();
573  $name = trim($name);
574  if ($timeTracker->LR) {
575  $timeTracker->push($TSkey, $name);
576  }
577  $fullConfigArray = [
578  'tempKey' => $name,
579  'tempKey.' => is_array($conf) ? $conf : [],
580  ];
581  // Resolve '=<' operator if needed
582  $fullConfigArray = $this->‪mergeTSRef($fullConfigArray, 'tempKey');
583  $contentObject = $this->‪getContentObject($fullConfigArray['tempKey']);
584  $content = '';
585  if ($contentObject) {
586  $content = $this->‪render($contentObject, $fullConfigArray['tempKey.']);
587  }
588  if ($timeTracker->LR) {
589  $timeTracker->pull($content);
590  }
591  return $content;
592  }
593 
600  public function ‪getContentObject($name): ?AbstractContentObject
601  {
602  $contentObjectFactory = $this->container
603  ? $this->container->get(ContentObjectFactory::class)
604  : GeneralUtility::makeInstance(ContentObjectFactory::class);
605  return $contentObjectFactory->getContentObject($name, $this->‪getRequest(), $this);
606  }
607 
608  /********************************************
609  *
610  * Functions rendering content objects (cObjects)
611  *
612  ********************************************/
623  public function ‪render(‪AbstractContentObject $contentObject, $configuration = []): string
624  {
625  $content = '';
626 
627  // Evaluate possible cache and return
628  $cacheConfiguration = $configuration['cache.'] ?? null;
629  if ($cacheConfiguration !== null) {
630  unset($configuration['cache.']);
631  $cache = $this->‪getFromCache($cacheConfiguration);
632  if ($cache !== false) {
633  return $cache;
634  }
635  }
636 
637  // Render content
638  try {
639  $content .= $contentObject->‪render($configuration);
640  } catch (‪ContentRenderingException $exception) {
641  // Content rendering Exceptions indicate a critical problem which should not be
642  // caught e.g. when something went wrong with Exception handling itself
643  throw $exception;
644  } catch (\‪Exception $exception) {
645  $exceptionHandler = $this->‪createExceptionHandler($configuration);
646  if ($exceptionHandler === null) {
647  throw $exception;
648  }
649  $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
650  }
651 
652  // Store cache
653  if ($cacheConfiguration !== null && $this->‪getRequest()->getAttribute('frontend.cache.instruction')->isCachingAllowed()) {
654  $key = $this->‪calculateCacheKey($cacheConfiguration);
655  if (!empty($key)) {
656  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('hash');
657  $tags = $this->‪calculateCacheTags($cacheConfiguration);
658  $lifetime = $this->‪calculateCacheLifetime($cacheConfiguration);
659  $cachedData = [
660  'content' => $content,
661  'cacheTags' => $tags,
662  ];
663  $cacheFrontend->set($key, $cachedData, $tags, $lifetime);
664  $this->‪getTypoScriptFrontendController()->addCacheTags($tags);
665  }
666  }
667 
668  return $content;
669  }
670 
677  protected function ‪createExceptionHandler(array $configuration): ?ExceptionHandlerInterface
678  {
679  $exceptionHandler = null;
680  $exceptionHandlerClassName = $this->‪determineExceptionHandlerClassName($configuration);
681  if (!empty($exceptionHandlerClassName)) {
682  $exceptionHandler = GeneralUtility::makeInstance($exceptionHandlerClassName);
683  if (!$exceptionHandler instanceof ‪ExceptionHandlerInterface) {
684  throw new ‪ContentRenderingException('An exception handler was configured but the class does not exist or does not implement the ExceptionHandlerInterface', 1403653369);
685  }
686  $exceptionHandler->setConfiguration($this->‪mergeExceptionHandlerConfiguration($configuration));
687  }
688  return $exceptionHandler;
689  }
690 
694  protected function ‪determineExceptionHandlerClassName(array $configuration): ?string
695  {
696  $typoScriptConfigArray = $this->‪getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
697  $exceptionHandlerClassName = null;
698  if (!isset($typoScriptConfigArray['contentObjectExceptionHandler'])) {
699  if (‪Environment::getContext()->isProduction()) {
700  $exceptionHandlerClassName = '1';
701  }
702  } else {
703  $exceptionHandlerClassName = $typoScriptConfigArray['contentObjectExceptionHandler'];
704  }
705  if (isset($configuration['exceptionHandler'])) {
706  $exceptionHandlerClassName = $configuration['exceptionHandler'];
707  }
708  if ($exceptionHandlerClassName === '1') {
709  $exceptionHandlerClassName = ProductionExceptionHandler::class;
710  }
711  return $exceptionHandlerClassName;
712  }
713 
718  protected function ‪mergeExceptionHandlerConfiguration(array $configuration): array
719  {
720  $exceptionHandlerConfiguration = [];
721  $typoScriptConfigArray = $this->‪getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
722  if (!empty($typoScriptConfigArray['contentObjectExceptionHandler.'])) {
723  $exceptionHandlerConfiguration = $typoScriptConfigArray['contentObjectExceptionHandler.'];
724  }
725  if (!empty($configuration['exceptionHandler.'])) {
726  $exceptionHandlerConfiguration = array_replace_recursive($exceptionHandlerConfiguration, $configuration['exceptionHandler.']);
727  }
728  return $exceptionHandlerConfiguration;
729  }
730 
739  public function ‪getUserObjectType()
740  {
742  }
743 
750  {
751  $this->userObjectType = ‪$userObjectType;
752  }
753 
757  public function ‪convertToUserIntObject()
758  {
759  if ($this->userObjectType !== self::OBJECTTYPE_USER) {
760  $this->‪getTimeTracker()->‪setTSlogMessage(self::class . '::convertToUserIntObject() is called in the wrong context or for the wrong object type', LogLevel::WARNING);
761  } else {
762  $this->doConvertToUserIntObject = true;
763  }
764  }
765 
766  /************************************
767  *
768  * Various helper functions for content objects:
769  *
770  ************************************/
778  public function ‪readFlexformIntoConf($flexData, &$conf, $recursive = false)
779  {
780  if ($recursive === false && is_string($flexData)) {
781  $flexData = ‪GeneralUtility::xml2array($flexData, 'T3');
782  }
783  if (is_array($flexData) && isset($flexData['data']['sDEF']['lDEF'])) {
784  $flexData = $flexData['data']['sDEF']['lDEF'];
785  }
786  if (!is_array($flexData)) {
787  return;
788  }
789  foreach ($flexData as $key => $value) {
790  if (!is_array($value)) {
791  continue;
792  }
793  if (isset($value['el'])) {
794  if (is_array($value['el']) && !empty($value['el'])) {
795  foreach ($value['el'] as $ekey => $element) {
796  if (isset($element['vDEF'])) {
797  $conf[$ekey] = $element['vDEF'];
798  } else {
799  if (is_array($element)) {
800  $this->‪readFlexformIntoConf($element, $conf[$key][key($element)][$ekey], true);
801  } else {
802  $this->‪readFlexformIntoConf($element, $conf[$key][$ekey], true);
803  }
804  }
805  }
806  } else {
807  $this->‪readFlexformIntoConf($value['el'], $conf[$key], true);
808  }
809  }
810  if (isset($value['vDEF'])) {
811  $conf[$key] = $value['vDEF'];
812  }
813  }
814  }
815 
824  public function ‪getSlidePids($pidList, $pidConf): string
825  {
826  // todo: phpstan states that $pidConf always exists and is not nullable. At the moment, this is a false positive
827  // as null can be passed into this method via $pidConf. As soon as more strict types are used, this isset
828  // check must be replaced with a more appropriate check like empty or count.
829  $pidList = isset($pidConf) ? trim((string)$this->‪stdWrap($pidList, $pidConf)) : trim($pidList);
830  if ($pidList === '') {
831  $pidList = 'this';
832  }
833  $pageRepository = $this->‪getPageRepository();
834  $listArr = null;
835  if (trim($pidList)) {
836  $contentPid = $this->‪getRequest()->getAttribute('frontend.page.information')->getContentFromPid();
837  $listArr = ‪GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $pidList));
838  $listArr = $this->‪checkPidArray($listArr);
839  }
840  $pidList = [];
841  if (is_array($listArr) && !empty($listArr)) {
842  foreach ($listArr as ‪$uid) {
843  $page = $pageRepository->getPage((int)‪$uid);
844  if (!$page['is_siteroot']) {
845  $pidList[] = $page['pid'];
846  }
847  }
848  }
849  return implode(',', $pidList);
850  }
851 
861  public function ‪imageLinkWrap($string, $imageFile, $conf)
862  {
863  $string = (string)$string;
864  $enable = $this->‪stdWrapValue('enable', $conf ?? []);
865  if (!$enable) {
866  return $string;
867  }
868  $content = (string)$this->‪typoLink($string, $conf['typolink.'] ?? []);
869  if (isset($conf['file.']) && is_scalar($imageFile)) {
870  $imageFile = $this->‪stdWrap((string)$imageFile, $conf['file.']);
871  }
872 
873  if ($imageFile instanceof File) {
874  $file = $imageFile;
875  } elseif ($imageFile instanceof FileReference) {
876  $file = $imageFile->getOriginalFile();
877  } else {
879  $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject((int)$imageFile);
880  } else {
881  $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObjectFromCombinedIdentifier($imageFile);
882  }
883  }
884 
885  // Create imageFileLink if not created with typolink
886  if ($content === $string && $file !== null) {
887  $parameterNames = ['width', 'height', 'effects', 'bodyTag', 'title', 'wrap', 'crop'];
888  ‪$parameters = [];
889  $sample = $this->‪stdWrapValue('sample', $conf ?? []);
890  if ($sample) {
891  ‪$parameters['sample'] = 1;
892  }
893  foreach ($parameterNames as $parameterName) {
894  if (isset($conf[$parameterName . '.'])) {
895  $conf[$parameterName] = $this->‪stdWrap($conf[$parameterName] ?? '', $conf[$parameterName . '.'] ?? []);
896  }
897  if (isset($conf[$parameterName]) && $conf[$parameterName]) {
898  ‪$parameters[$parameterName] = $conf[$parameterName];
899  }
900  }
901  $parametersEncoded = base64_encode((string)json_encode(‪$parameters));
902  $hashService = GeneralUtility::makeInstance(HashService::class);
903  $hmac = $hashService->hmac(implode('|', [$file->getUid(), $parametersEncoded]), 'tx_cms_showpic');
904  $params = '&md5=' . $hmac;
905  foreach (str_split($parametersEncoded, 64) as $index => $chunk) {
906  $params .= '&parameters' . rawurlencode('[') . $index . rawurlencode(']') . '=' . rawurlencode($chunk);
907  }
908  ‪$url = $this->‪getTypoScriptFrontendController()->absRefPrefix . 'index.php?eID=tx_cms_showpic&file=' . $file->getUid() . $params;
909  $directImageLink = $this->‪stdWrapValue('directImageLink', $conf ?? []);
910  if ($directImageLink) {
911  $imgResourceConf = [
912  'file' => $imageFile,
913  'file.' => $conf,
914  ];
915  ‪$url = $this->‪cObjGetSingle('IMG_RESOURCE', $imgResourceConf);
916  if (!‪$url) {
917  // If no imagemagick / gm is available
918  ‪$url = $imageFile;
919  }
920  }
921  $target = (string)$this->‪stdWrapValue('target', $conf ?? []);
922  if ($target === '') {
923  $target = 'thePicture';
924  }
925  $a1 = '';
926  $a2 = '';
927  $conf['JSwindow'] = $this->‪stdWrapValue('JSwindow', $conf ?? []);
928  if ($conf['JSwindow']) {
929  $altUrl = $this->‪stdWrapValue('altUrl', $conf['JSwindow.'] ?? []);
930  if ($altUrl) {
931  ‪$url = $altUrl . (($conf['JSwindow.']['altUrl_noDefaultParams'] ?? false) ? '' : '?file=' . rawurlencode((string)$imageFile) . $params);
932  }
933 
934  $processedFile = $file->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $conf);
935  $JSwindowExpand = $this->‪stdWrapValue('expand', $conf['JSwindow.'] ?? []);
936  $offset = ‪GeneralUtility::intExplode(',', $JSwindowExpand . ',');
937  $newWindow = $this->‪stdWrapValue('newWindow', $conf['JSwindow.'] ?? []);
938  $params = [
939  'width' => ($processedFile->getProperty('width') + $offset[0]),
940  'height' => ($processedFile->getProperty('height') + $offset[1]),
941  'status' => '0',
942  'menubar' => '0',
943  ];
944  // params override existing parameters from above, or add more
945  $windowParams = (string)$this->‪stdWrapValue('params', $conf['JSwindow.'] ?? []);
946  $windowParams = explode(',', $windowParams);
947  foreach ($windowParams as $windowParam) {
948  $windowParamParts = explode('=', $windowParam);
949  $paramKey = $windowParamParts[0];
950  $paramValue = $windowParamParts[1] ?? null;
951 
952  if ($paramKey === '') {
953  continue;
954  }
955 
956  if ($paramValue !== '') {
957  $params[$paramKey] = $paramValue;
958  } else {
959  unset($params[$paramKey]);
960  }
961  }
962  $paramString = '';
963  foreach ($params as $paramKey => $paramValue) {
964  $paramString .= htmlspecialchars($paramKey) . '=' . htmlspecialchars((string)$paramValue) . ',';
965  }
966 
967  $attrs = [
968  'href' => (string)‪$url,
969  'data-window-url' => ‪$url,
970  'data-window-target' => $newWindow ? md5((string)‪$url) : 'thePicture',
971  'data-window-features' => rtrim($paramString, ','),
972  ];
973  if ($target !== '') {
974  $attrs['target'] = $target;
975  }
976 
977  $typoScriptConfigArray = $this->‪getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
978  $a1 = sprintf(
979  '<a %s%s>',
980  GeneralUtility::implodeAttributes($attrs, true),
981  trim($typoScriptConfigArray['ATagParams'] ?? '') ? ' ' . trim($typoScriptConfigArray['ATagParams']) : ''
982  );
983  $a2 = '</a>';
984  $this->addDefaultFrontendJavaScript($this->‪getRequest());
985  } else {
986  $conf['linkParams.']['directImageLink'] = (bool)($conf['directImageLink'] ?? false);
987  $conf['linkParams.']['parameter'] = ‪$url;
988  $string = (string)$this->‪typoLink($string, $conf['linkParams.']);
989  }
990  if (isset($conf['stdWrap.'])) {
991  $string = (string)$this->‪stdWrap($string, $conf['stdWrap.']);
992  }
993  $content = $a1 . $string . $a2;
994  }
995  return $content;
996  }
997 
1006  public function ‪lastChanged($tstamp)
1007  {
1008  $tstamp = (int)$tstamp;
1009  $tsfe = $this->‪getTypoScriptFrontendController();
1010  if ($tstamp > (int)($tsfe->register['SYS_LASTCHANGED'] ?? 0)) {
1011  $tsfe->register['SYS_LASTCHANGED'] = $tstamp;
1012  }
1013  }
1014 
1015  /***********************************************
1016  *
1017  * HTML template processing functions
1018  *
1019  ***********************************************/
1026  public function ‪setCurrentFile($fileObject)
1027  {
1028  $this->currentFile = $fileObject;
1029  }
1030 
1036  public function ‪getCurrentFile()
1037  {
1038  return ‪$this->currentFile;
1039  }
1040 
1041  /***********************************************
1042  *
1043  * "stdWrap" + sub functions
1044  *
1045  ***********************************************/
1055  public function ‪stdWrap($content = '', $conf = [])
1056  {
1057  $content = (string)$content;
1058  if (!is_array($conf) || !$conf) {
1059  return $content;
1060  }
1061 
1062  // Activate the stdWrap PSR-14 Events - They will be executed
1063  // as stdWrap functions, based on the defined "stdWrapOrder".
1064  $conf[BeforeStdWrapFunctionsInitializedEvent::class] = 1;
1065  $conf[AfterStdWrapFunctionsInitializedEvent::class] = 1;
1066  $conf[BeforeStdWrapFunctionsExecutedEvent::class] = 1;
1067  $conf[AfterStdWrapFunctionsExecutedEvent::class] = 1;
1068  $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
1069 
1070  // Cache handling
1071  if (isset($conf['cache.']) && is_array($conf['cache.'])) {
1072  $conf['cache.']['key'] = $this->‪stdWrapValue('key', $conf['cache.'] ?? []);
1073  $conf['cache.']['tags'] = $this->‪stdWrapValue('tags', $conf['cache.'] ?? []);
1074  $conf['cache.']['lifetime'] = $this->‪stdWrapValue('lifetime', $conf['cache.'] ?? []);
1075  $conf['cacheRead'] = 1;
1076  $conf['cacheStore'] = 1;
1077  }
1078  // The configuration is sorted and filtered by intersection with the defined stdWrapOrder.
1079  $sortedConf = array_keys(array_intersect_key($this->stdWrapOrder, $conf));
1080  // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
1081  $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
1082  // Additional Array to check whether a function has already been executed
1083  $isExecuted = [];
1084  // Additional switch to make sure 'required', 'if' and 'fieldRequired'
1085  // will still stop rendering immediately in case they return FALSE
1086  $this->stdWrapRecursionLevel++;
1087  $this->stopRendering[‪$this->stdWrapRecursionLevel] = false;
1088  // execute each function in the predefined order
1089  foreach ($sortedConf as $stdWrapName) {
1090  // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
1091  if ((!isset($isExecuted[$stdWrapName]) || !$isExecuted[$stdWrapName]) && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
1092  $functionName = rtrim($stdWrapName, '.');
1093  $functionProperties = $functionName . '.';
1094  $functionType = $this->stdWrapOrder[$functionName] ?? '';
1095  // If there is any code on the next level, check if it contains "official" stdWrap functions
1096  // if yes, execute them first - will make each function stdWrap aware
1097  // so additional stdWrap calls within the functions can be removed, since the result will be the same
1098  if (!empty($conf[$functionProperties]) && !‪GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
1099  if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
1100  // Check if there's already content available before processing
1101  // any ifEmpty or ifBlank stdWrap properties
1102  if (($functionName === 'ifBlank' && $content !== '') ||
1103  ($functionName === 'ifEmpty' && !empty(trim((string)$content)))) {
1104  continue;
1105  }
1106 
1107  $conf[$functionName] = $this->‪stdWrap($conf[$functionName] ?? '', $conf[$functionProperties]);
1108  }
1109  }
1110  // Check if key is still containing something, since it might have been changed by next level stdWrap before
1111  if ((isset($conf[$functionName]) || ($conf[$functionProperties] ?? null))
1112  && ($functionType !== 'boolean' || ($conf[$functionName] ?? null))
1113  ) {
1114  // Get just that part of $conf that is needed for the particular function
1115  $singleConf = [
1116  $functionName => $conf[$functionName] ?? null,
1117  $functionProperties => $conf[$functionProperties] ?? null,
1118  ];
1119  // Hand over the whole $conf array to the hooks
1120  if ($functionType === 'hook') {
1121  $singleConf = $conf;
1122  }
1123  // Add both keys - with and without the dot - to the set of executed functions
1124  $isExecuted[$functionName] = true;
1125  $isExecuted[$functionProperties] = true;
1126  if ($functionType === 'event') {
1127  $content = $eventDispatcher->dispatch(
1128  new $functionName($content, $conf, $this)
1129  )->getContent();
1130  } else {
1131  // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
1132  $functionName = 'stdWrap_' . $functionName;
1133  $content = $this->{$functionName}($content, $singleConf);
1134  }
1135  } elseif ($functionType === 'boolean' && !($conf[$functionName] ?? null)) {
1136  $isExecuted[$functionName] = true;
1137  $isExecuted[$functionProperties] = true;
1138  }
1139  }
1140  }
1141  unset($this->stopRendering[$this->stdWrapRecursionLevel]);
1142  $this->stdWrapRecursionLevel--;
1143 
1144  return $content;
1145  }
1146 
1155  public function ‪stdWrapValue($key, array $config, $defaultValue = '')
1156  {
1157  if (isset($config[$key])) {
1158  if (!isset($config[$key . '.'])) {
1159  return $config[$key];
1160  }
1161  } elseif (isset($config[$key . '.'])) {
1162  $config[$key] = '';
1163  } else {
1164  return $defaultValue;
1165  }
1166  $stdWrapped = $this->‪stdWrap($config[$key], $config[$key . '.']);
1167  // The string "0" should be returned.
1168  return $stdWrapped !== '' ? $stdWrapped : $defaultValue;
1169  }
1170 
1178  public function ‪stdWrap_cacheRead($content = '', $conf = [])
1179  {
1180  if (!isset($conf['cache.'])) {
1181  return $content;
1182  }
1183  $result = $this->‪getFromCache($conf['cache.']);
1184  return $result === false ? $content : $result;
1185  }
1186 
1194  public function ‪stdWrap_addPageCacheTags($content = '', $conf = [])
1195  {
1196  $tags = (string)$this->‪stdWrapValue('addPageCacheTags', $conf ?? []);
1197  if (!empty($tags)) {
1198  $cacheTags = ‪GeneralUtility::trimExplode(',', $tags, true);
1199  $this->‪getTypoScriptFrontendController()->addCacheTags($cacheTags);
1200  }
1201  return $content;
1202  }
1203 
1211  public function ‪stdWrap_setContentToCurrent($content = '')
1212  {
1213  $this->data[‪$this->currentValKey] = $content;
1214  return $content;
1215  }
1216 
1225  public function ‪stdWrap_setCurrent($content = '', $conf = [])
1226  {
1227  $this->data[‪$this->currentValKey] = $conf['setCurrent'] ?? null;
1228  return $content;
1229  }
1230 
1239  public function ‪stdWrap_lang($content = '', $conf = [])
1240  {
1241  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
1242  $siteLanguage = $this->‪getRequest()->getAttribute('language') ?? $this->‪getRequest()->getAttribute('site')->getDefaultLanguage();
1243  $currentLanguageCode = $siteLanguage->getTypo3Language();
1244  if (!$currentLanguageCode) {
1245  return $content;
1246  }
1247  if (isset($conf['lang.'][$currentLanguageCode])) {
1248  $content = $conf['lang.'][$currentLanguageCode];
1249  } else {
1250  // @todo: use the Locale object and its dependencies in TYPO3 v13
1251  // Check language dependencies
1252  $locales = GeneralUtility::makeInstance(Locales::class);
1253  foreach ($locales->getLocaleDependencies($currentLanguageCode) as $languageCode) {
1254  if (isset($conf['lang.'][$languageCode])) {
1255  $content = $conf['lang.'][$languageCode];
1256  break;
1257  }
1258  }
1259  }
1260  return $content;
1261  }
1262 
1270  public function ‪stdWrap_data($_ = '', $conf = [])
1271  {
1272  return $this->‪getData($conf['data'], $this->data);
1273  }
1274 
1283  public function ‪stdWrap_field($content = '', $conf = [])
1284  {
1285  return $this->‪getFieldVal($conf['field']);
1286  }
1287 
1297  public function ‪stdWrap_current($content = '', $conf = [])
1298  {
1299  return $this->‪getCurrentVal();
1300  }
1301 
1311  public function ‪stdWrap_cObject($content = '', $conf = [])
1312  {
1313  return $this->‪cObjGetSingle($conf['cObject'] ?? '', $conf['cObject.'] ?? [], '/stdWrap/.cObject');
1314  }
1315 
1325  public function ‪stdWrap_numRows($content = '', $conf = [])
1326  {
1327  return $this->‪numRows($conf['numRows.']);
1328  }
1329 
1338  public function ‪stdWrap_preUserFunc($content = '', $conf = [])
1339  {
1340  return $this->‪callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'] ?? [], $content);
1341  }
1342 
1351  public function ‪stdWrap_override($content = '', $conf = [])
1352  {
1353  if (trim($conf['override'] ?? false)) {
1354  $content = $conf['override'];
1355  }
1356  return $content;
1357  }
1358 
1368  public function ‪stdWrap_preIfEmptyListNum($content = '', $conf = [])
1369  {
1370  return $this->‪listNum($content, $conf['preIfEmptyListNum'] ?? '0', $conf['preIfEmptyListNum.']['splitChar'] ?? ',');
1371  }
1372 
1381  public function ‪stdWrap_ifNull($content = '', $conf = [])
1382  {
1383  return $content ?? $conf['ifNull'];
1384  }
1385 
1395  public function ‪stdWrap_ifEmpty($content = '', $conf = [])
1396  {
1397  if (empty(trim((string)$content))) {
1398  $content = $conf['ifEmpty'];
1399  }
1400  return $content;
1401  }
1402 
1412  public function ‪stdWrap_ifBlank($content = '', $conf = [])
1413  {
1414  if (trim((string)$content) === '') {
1415  $content = $conf['ifBlank'];
1416  }
1417  return $content;
1418  }
1419 
1430  public function ‪stdWrap_listNum($content = '', $conf = [])
1431  {
1432  return $this->‪listNum($content, $conf['listNum'] ?? '0', $conf['listNum.']['splitChar'] ?? ',');
1433  }
1434 
1442  public function ‪stdWrap_trim($content = '')
1443  {
1444  return trim((string)$content);
1445  }
1446 
1455  public function ‪stdWrap_strPad($content = '', $conf = [])
1456  {
1457  // Must specify a length in conf for this to make sense
1458  $length = (int)$this->‪stdWrapValue('length', $conf['strPad.'] ?? [], 0);
1459  // Padding with space is PHP-default
1460  $padWith = (string)$this->‪stdWrapValue('padWith', $conf['strPad.'] ?? [], ' ');
1461  // Padding on the right side is PHP-default
1462  $padType = STR_PAD_RIGHT;
1463 
1464  if (!empty($conf['strPad.']['type'])) {
1465  $type = (string)$this->‪stdWrapValue('type', $conf['strPad.'] ?? []);
1466  if (strtolower($type) === 'left') {
1467  $padType = STR_PAD_LEFT;
1468  } elseif (strtolower($type) === 'both') {
1469  $padType = STR_PAD_BOTH;
1470  }
1471  }
1472  return ‪StringUtility::multibyteStringPad($content, $length, $padWith, $padType);
1473  }
1474 
1486  public function ‪stdWrap_stdWrap($content = '', $conf = [])
1487  {
1488  return $this->‪stdWrap($content, $conf['stdWrap.']);
1489  }
1490 
1499  public function ‪stdWrap_required($content = '')
1500  {
1501  if ((string)$content === '') {
1502  $content = '';
1503  $this->stopRendering[‪$this->stdWrapRecursionLevel] = true;
1504  }
1505  return $content;
1506  }
1507 
1517  public function ‪stdWrap_if($content = '', $conf = [])
1518  {
1519  if (empty($conf['if.']) || $this->‪checkIf($conf['if.'])) {
1520  return $content;
1521  }
1522  $this->stopRendering[‪$this->stdWrapRecursionLevel] = true;
1523  return '';
1524  }
1525 
1535  public function ‪stdWrap_fieldRequired($content = '', $conf = [])
1536  {
1537  if (!trim($this->data[$conf['fieldRequired'] ?? null] ?? '')) {
1538  $content = '';
1539  $this->stopRendering[‪$this->stdWrapRecursionLevel] = true;
1540  }
1541  return $content;
1542  }
1543 
1554  public function ‪stdWrap_csConv($content = '', $conf = [])
1555  {
1556  if (!empty($conf['csConv'])) {
1557  ‪$output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['csConv'])));
1558  return ‪$output !== false && ‪$output !== '' ? ‪$output : $content;
1559  }
1560  return $content;
1561  }
1562 
1572  public function ‪stdWrap_parseFunc($content = '', $conf = [])
1573  {
1574  return $this->‪parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
1575  }
1576 
1586  public function ‪stdWrap_HTMLparser($content = '', $conf = [])
1587  {
1588  if (isset($conf['HTMLparser.']) && is_array($conf['HTMLparser.'])) {
1589  $content = $this->‪HTMLparser_TSbridge($content, $conf['HTMLparser.']);
1590  }
1591  return $content;
1592  }
1593 
1603  public function ‪stdWrap_split($content = '', $conf = [])
1604  {
1605  return $this->‪splitObj($content, $conf['split.']);
1606  }
1607 
1616  public function ‪stdWrap_replacement($content = '', $conf = [])
1617  {
1618  return $this->‪replacement($content, $conf['replacement.']);
1619  }
1620 
1630  public function ‪stdWrap_prioriCalc($content = '', $conf = [])
1631  {
1632  $content = ‪MathUtility::calculateWithParentheses($content);
1633  if (!empty($conf['prioriCalc']) && $conf['prioriCalc'] === 'intval') {
1634  $content = (int)$content;
1635  }
1636  return $content;
1637  }
1638 
1650  public function ‪stdWrap_char($content = '', $conf = [])
1651  {
1652  return chr((int)$conf['char']);
1653  }
1654 
1662  public function ‪stdWrap_intval($content = '')
1663  {
1664  return (int)$content;
1665  }
1666 
1675  public function ‪stdWrap_hash($content = '', array $conf = [])
1676  {
1677  $algorithm = (string)$this->‪stdWrapValue('hash', $conf ?? []);
1678  if (in_array($algorithm, hash_algos())) {
1679  return hash($algorithm, $content);
1680  }
1681  // Non-existing hashing algorithm
1682  return '';
1683  }
1684 
1693  public function ‪stdWrap_round($content = '', $conf = [])
1694  {
1695  return $this->‪round($content, $conf['round.']);
1696  }
1697 
1706  public function ‪stdWrap_numberFormat($content = '', $conf = [])
1707  {
1708  return $this->‪numberFormat((float)$content, $conf['numberFormat.'] ?? []);
1709  }
1710 
1718  public function ‪stdWrap_expandList($content = '')
1719  {
1720  return ‪GeneralUtility::expandList($content);
1721  }
1722 
1732  public function ‪stdWrap_date($content = '', $conf = [])
1733  {
1734  // Check for zero length string to mimic default case of date/gmdate.
1735  $content = (string)$content === '' ? ‪$GLOBALS['EXEC_TIME'] : (int)$content;
1736  $content = !empty($conf['date.']['GMT']) ? gmdate($conf['date'] ?? null, $content) : date($conf['date'] ?? null, $content);
1737  return $content;
1738  }
1739 
1749  public function ‪stdWrap_strftime($content = '', $conf = [])
1750  {
1751  // Check for zero length string to mimic default case of strtime/gmstrftime
1752  $content = (string)$content === '' ? ‪$GLOBALS['EXEC_TIME'] : (int)$content;
1753  $content = (isset($conf['strftime.']['GMT']) && $conf['strftime.']['GMT'])
1754  ? (new ‪DateFormatter())->strftime($conf['strftime'] ?? '', $content, null, true)
1755  : (new ‪DateFormatter())->strftime($conf['strftime'] ?? '', $content);
1756  if (!empty($conf['strftime.']['charset'])) {
1757  ‪$output = mb_convert_encoding((string)$content, 'utf-8', trim(strtolower($conf['strftime.']['charset'])));
1758  return ‪$output ?: $content;
1759  }
1760  return $content;
1761  }
1762 
1771  public function ‪stdWrap_strtotime($content = '', $conf = [])
1772  {
1773  if ($conf['strtotime'] !== '1') {
1774  $content .= ' ' . $conf['strtotime'];
1775  }
1776  return strtotime($content, ‪$GLOBALS['EXEC_TIME']);
1777  }
1778 
1788  public function ‪stdWrap_formattedDate(string $content, array $conf): string
1789  {
1790  $pattern = $conf['formattedDate'] ?? 'LONG';
1791  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
1792  $language = $this->‪getRequest()->getAttribute('language') ?? $this->‪getRequest()->getAttribute('site')->getDefaultLanguage();
1793  $locale = $conf['formattedDate.']['locale'] ?? $language->getLocale();
1794 
1795  if ($content === '' || $content === '0') {
1796  $content = GeneralUtility::makeInstance(Context::class)->getAspect('date')->getDateTime();
1797  } else {
1798  // format this to a timestamp now
1799  $content = strtotime((‪MathUtility::canBeInterpretedAsInteger($content) ? '@' : '') . $content);
1800  if ($content === false) {
1801  $content = GeneralUtility::makeInstance(Context::class)->getAspect('date')->getDateTime();
1802  }
1803  }
1804  return (new DateFormatter())->format($content, $pattern, $locale);
1805  }
1806 
1815  public function ‪stdWrap_age($content = '', $conf = [])
1816  {
1817  return $this->‪calcAge((int)(‪$GLOBALS['EXEC_TIME'] ?? 0) - (int)$content, $conf['age'] ?? null);
1818  }
1819 
1829  public function ‪stdWrap_case($content = '', $conf = [])
1830  {
1831  return $this->‪HTMLcaseshift($content, $conf['case']);
1832  }
1833 
1842  public function ‪stdWrap_bytes($content = '', $conf = [])
1843  {
1844  return GeneralUtility::formatSize((int)$content, $conf['bytes.']['labels'] ?? '', $conf['bytes.']['base'] ?? 0);
1845  }
1846 
1855  public function ‪stdWrap_substring($content = '', $conf = [])
1856  {
1857  return $this->‪substring($content, $conf['substring']);
1858  }
1859 
1868  public function ‪stdWrap_cropHTML($content = '', $conf = [])
1869  {
1870  return $this->‪cropHTML($content, $conf['cropHTML'] ?? '');
1871  }
1872 
1880  public function ‪stdWrap_stripHtml($content = '')
1881  {
1882  return strip_tags($content);
1883  }
1884 
1893  public function ‪stdWrap_crop($content = '', $conf = [])
1894  {
1895  return $this->‪crop($content, $conf['crop']);
1896  }
1897 
1905  public function ‪stdWrap_rawUrlEncode($content = '')
1906  {
1907  return rawurlencode($content);
1908  }
1909 
1919  public function ‪stdWrap_htmlSpecialChars($content = '', $conf = [])
1920  {
1921  if (!empty($conf['htmlSpecialChars.']['preserveEntities'])) {
1922  $content = htmlspecialchars($content, ENT_COMPAT, 'UTF-8', false);
1923  } else {
1924  $content = htmlspecialchars($content);
1925  }
1926  return $content;
1927  }
1928 
1936  public function ‪stdWrap_encodeForJavaScriptValue($content = '')
1937  {
1938  return GeneralUtility::quoteJSvalue($content);
1939  }
1940 
1949  public function ‪stdWrap_doubleBrTag($content = '', $conf = [])
1950  {
1951  return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'] ?? '', $content);
1952  }
1953 
1962  public function ‪stdWrap_br($content = '')
1963  {
1964  $docType = GeneralUtility::makeInstance(PageRenderer::class)->getDocType();
1965  return nl2br($content, $docType->isXmlCompliant());
1966  }
1967 
1976  public function ‪stdWrap_brTag($content = '', $conf = [])
1977  {
1978  return str_replace(LF, (string)($conf['brTag'] ?? ''), $content);
1979  }
1980 
1990  public function ‪stdWrap_encapsLines($content = '', $conf = [])
1991  {
1992  return $this->‪encaps_lineSplit($content, $conf['encapsLines.']);
1993  }
1994 
2002  public function ‪stdWrap_keywords($content = '')
2003  {
2004  return $this->‪keywords($content);
2005  }
2006 
2016  public function ‪stdWrap_innerWrap($content = '', $conf = [])
2017  {
2018  return $this->‪wrap($content, $conf['innerWrap'] ?? null);
2019  }
2020 
2030  public function ‪stdWrap_innerWrap2($content = '', $conf = [])
2031  {
2032  return $this->‪wrap($content, $conf['innerWrap2'] ?? null);
2033  }
2034 
2043  public function ‪stdWrap_preCObject($content = '', $conf = [])
2044  {
2045  return $this->‪cObjGetSingle($conf['preCObject'], $conf['preCObject.'], '/stdWrap/.preCObject') . $content;
2046  }
2047 
2056  public function ‪stdWrap_postCObject($content = '', $conf = [])
2057  {
2058  return $content . $this->‪cObjGetSingle($conf['postCObject'], $conf['postCObject.'], '/stdWrap/.postCObject');
2059  }
2060 
2070  public function ‪stdWrap_wrapAlign($content = '', $conf = [])
2071  {
2072  $wrapAlign = trim($conf['wrapAlign'] ?? '');
2073  if ($wrapAlign) {
2074  $content = $this->‪wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
2075  }
2076  return $content;
2077  }
2078 
2089  public function ‪stdWrap_typolink($content = '', $conf = [])
2090  {
2091  return $this->‪typoLink((string)$content, $conf['typolink.'] ?? []);
2092  }
2093 
2106  public function ‪stdWrap_wrap($content = '', $conf = [])
2107  {
2108  return $this->‪wrap(
2109  $content,
2110  $conf['wrap'] ?? null,
2111  $conf['wrap.']['splitChar'] ?? '|'
2112  );
2113  }
2114 
2124  public function ‪stdWrap_noTrimWrap($content = '', $conf = [])
2125  {
2126  $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
2127  ? $this->‪stdWrap($conf['noTrimWrap.']['splitChar'] ?? '', $conf['noTrimWrap.']['splitChar.'])
2128  : $conf['noTrimWrap.']['splitChar'] ?? '';
2129  if ($splitChar === null || $splitChar === '') {
2130  $splitChar = '|';
2131  }
2132  $content = $this->‪noTrimWrap(
2133  $content,
2134  $conf['noTrimWrap'],
2135  $splitChar
2136  );
2137  return $content;
2138  }
2139 
2149  public function ‪stdWrap_wrap2($content = '', $conf = [])
2150  {
2151  return $this->‪wrap(
2152  $content,
2153  $conf['wrap2'] ?? null,
2154  $conf['wrap2.']['splitChar'] ?? '|'
2155  );
2156  }
2157 
2167  public function ‪stdWrap_dataWrap($content = '', $conf = [])
2168  {
2169  return $this->‪dataWrap($content, $conf['dataWrap']);
2170  }
2171 
2180  public function ‪stdWrap_prepend($content = '', $conf = [])
2181  {
2182  return $this->‪cObjGetSingle($conf['prepend'], $conf['prepend.'], '/stdWrap/.prepend') . $content;
2183  }
2184 
2193  public function ‪stdWrap_append($content = '', $conf = [])
2194  {
2195  return $content . $this->‪cObjGetSingle($conf['append'], $conf['append.'], '/stdWrap/.append');
2196  }
2197 
2207  public function ‪stdWrap_wrap3($content = '', $conf = [])
2208  {
2209  return $this->‪wrap(
2210  $content,
2211  $conf['wrap3'] ?? null,
2212  $conf['wrap3.']['splitChar'] ?? '|'
2213  );
2214  }
2215 
2224  public function ‪stdWrap_orderedStdWrap($content = '', $conf = [])
2225  {
2226  $sortedKeysArray = ArrayUtility::filterAndSortByNumericKeys($conf['orderedStdWrap.'], true);
2227  foreach ($sortedKeysArray as $key) {
2228  $content = (string)$this->‪stdWrap($content, $conf['orderedStdWrap.'][$key . '.'] ?? null);
2229  }
2230  return $content;
2231  }
2232 
2241  public function ‪stdWrap_outerWrap($content = '', $conf = [])
2242  {
2243  return $this->‪wrap($content, $conf['outerWrap'] ?? null);
2244  }
2245 
2253  public function ‪stdWrap_insertData($content = '')
2254  {
2255  return $this->‪insertData($content);
2256  }
2257 
2266  public function ‪stdWrap_postUserFunc($content = '', $conf = [])
2267  {
2268  return $this->‪callUserFunction($conf['postUserFunc'], $conf['postUserFunc.'] ?? [], $content);
2269  }
2270 
2280  public function ‪stdWrap_postUserFuncInt($content = '', $conf = [])
2281  {
2282  $substKey = 'INT_SCRIPT.' . $this->‪getTypoScriptFrontendController()->uniqueHash();
2283  $this->‪getTypoScriptFrontendController()->config['INTincScript'][$substKey] = [
2284  'content' => $content,
2285  'postUserFunc' => $conf['postUserFuncInt'],
2286  'conf' => $conf['postUserFuncInt.'],
2287  'type' => 'POSTUSERFUNC',
2288  'cObj' => serialize($this),
2289  ];
2290  $content = '<!--' . $substKey . '-->';
2291  return $content;
2292  }
2293 
2302  public function ‪stdWrap_prefixComment($content = '', $conf = [])
2303  {
2304  $typoScriptConfigArray = $this->‪getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
2305  if (
2306  (!isset($typoScriptConfigArray['disablePrefixComment']) || !$typoScriptConfigArray['disablePrefixComment'])
2307  && !empty($conf['prefixComment'])
2308  ) {
2309  $content = $this->‪prefixComment($conf['prefixComment'], [], $content);
2310  }
2311  return $content;
2312  }
2313 
2314  public function ‪stdWrap_htmlSanitize(string $content = '', array $conf = []): string
2315  {
2316  $build = $conf['build'] ?? 'default';
2317  if (class_exists($build) && is_a($build, BuilderInterface::class, true)) {
2318  $builder = GeneralUtility::makeInstance($build);
2319  } else {
2320  $factory = GeneralUtility::makeInstance(SanitizerBuilderFactory::class);
2321  $builder = $factory->build($build);
2322  }
2323  $sanitizer = $builder->build();
2324  $initiator = $this->‪shallDebug()
2325  ? GeneralUtility::makeInstance(SanitizerInitiator::class, ‪DebugUtility::debugTrail())
2326  : null;
2327  return $sanitizer->sanitize($content, $initiator);
2328  }
2329 
2337  public function ‪stdWrap_cacheStore($content = '', $conf = []): ?string
2338  {
2339  if (!isset($conf['cache.'])) {
2340  return $content;
2341  }
2342  $key = $this->‪calculateCacheKey($conf['cache.']);
2343  if (empty($key)) {
2344  return $content;
2345  }
2346 
2347  $event = GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
2348  new BeforeStdWrapContentStoredInCacheEvent(
2349  content: $content,
2350  tags: $this->‪calculateCacheTags($conf['cache.']),
2351  key: (string)$key,
2352  lifetime: $this->‪calculateCacheLifetime($conf['cache.']),
2353  configuration: $conf,
2354  contentObjectRenderer: $this
2355  )
2356  );
2357 
2358  GeneralUtility::makeInstance(CacheManager::class)
2359  ->getCache('hash')
2360  ->set(
2361  $event->getKey(),
2362  ['content' => $event->getContent(), 'cacheTags' => $event->getTags()],
2363  $event->getTags(),
2364  $event->getLifetime()
2365  );
2366  $this->‪getTypoScriptFrontendController()->addCacheTags($event->getTags());
2367  return $event->getContent();
2368  }
2369 
2377  public function ‪stdWrap_debug($content = '')
2378  {
2379  return '<pre>' . htmlspecialchars($content) . '</pre>';
2380  }
2381 
2390  public function ‪stdWrap_debugFunc($content = '', $conf = [])
2391  {
2392  ‪debug((int)$conf['debugFunc'] === 2 ? [$content] : $content);
2393  return $content;
2394  }
2395 
2403  public function ‪stdWrap_debugData($content = '')
2404  {
2405  ‪debug($this->data, '$cObj->data:');
2406  return $content;
2407  }
2408 
2418  public function ‪numRows($conf)
2419  {
2420  $conf['select.']['selectFields'] = 'count(*)';
2421  $statement = $this->‪exec_getQuery($conf['table'], $conf['select.']);
2422 
2423  return (int)$statement->fetchOne();
2424  }
2425 
2434  public function ‪listNum($content, $listNum, $delimeter = ',')
2435  {
2436  $delimeter = $delimeter ?: ',';
2438  $delimeter = chr((int)$delimeter);
2439  }
2440  $temp = explode($delimeter, $content);
2441  if ($temp === ['']) {
2442  return '';
2443  }
2444  $last = '' . (count($temp) - 1);
2445  // Take a random item if requested
2446  if ($listNum === 'rand') {
2447  $listNum = (string)random_int(0, count($temp) - 1);
2448  }
2449  $index = $this->‪calc(str_ireplace('last', $last, $listNum));
2450  return $temp[$index] ?? '';
2451  }
2452 
2459  public function ‪checkIf($conf): bool
2460  {
2461  if (!is_array($conf)) {
2462  return true;
2463  }
2464  if (isset($conf['directReturn'])) {
2465  return (bool)$conf['directReturn'];
2466  }
2467  $flag = true;
2468  if (isset($conf['isNull.'])) {
2469  $isNull = $this->‪stdWrap('', $conf['isNull.']);
2470  if ($isNull !== null) {
2471  $flag = false;
2472  }
2473  }
2474  if (isset($conf['isTrue']) || isset($conf['isTrue.'])) {
2475  $isTrue = trim((string)$this->‪stdWrapValue('isTrue', $conf));
2476  if (!$isTrue) {
2477  $flag = false;
2478  }
2479  }
2480  if (isset($conf['isFalse']) || isset($conf['isFalse.'])) {
2481  $isFalse = trim((string)$this->‪stdWrapValue('isFalse', $conf));
2482  if ($isFalse) {
2483  $flag = false;
2484  }
2485  }
2486  if (isset($conf['isPositive']) || isset($conf['isPositive.'])) {
2487  $number = $this->‪calc((string)$this->‪stdWrapValue('isPositive', $conf));
2488  if ($number < 1) {
2489  $flag = false;
2490  }
2491  }
2492  if ($flag) {
2493  $comparisonValue = trim((string)$this->‪stdWrapValue('value', $conf));
2494  if (isset($conf['isGreaterThan']) || isset($conf['isGreaterThan.'])) {
2495  $number = trim((string)$this->‪stdWrapValue('isGreaterThan', $conf));
2496  if ($number <= $comparisonValue) {
2497  $flag = false;
2498  }
2499  }
2500  if (isset($conf['isLessThan']) || isset($conf['isLessThan.'])) {
2501  $number = trim((string)$this->‪stdWrapValue('isLessThan', $conf));
2502  if ($number >= $comparisonValue) {
2503  $flag = false;
2504  }
2505  }
2506  if (isset($conf['equals']) || isset($conf['equals.'])) {
2507  $number = trim((string)$this->‪stdWrapValue('equals', $conf));
2508  if ($number != $comparisonValue) {
2509  $flag = false;
2510  }
2511  }
2512  if (isset($conf['contains']) || isset($conf['contains.'])) {
2513  $needle = trim((string)$this->‪stdWrapValue('contains', $conf));
2514  if (!str_contains($comparisonValue, $needle)) {
2515  $flag = false;
2516  }
2517  }
2518  if (isset($conf['startsWith']) || isset($conf['startsWith.'])) {
2519  $needle = trim((string)$this->‪stdWrapValue('startsWith', $conf));
2520  if (!str_starts_with($comparisonValue, $needle)) {
2521  $flag = false;
2522  }
2523  }
2524  if (isset($conf['endsWith']) || isset($conf['endsWith.'])) {
2525  $needle = trim((string)$this->‪stdWrapValue('endsWith', $conf));
2526  if (!str_ends_with($comparisonValue, $needle)) {
2527  $flag = false;
2528  }
2529  }
2530  if (isset($conf['isInList']) || isset($conf['isInList.'])) {
2531  $singleValueWhichNeedsToBeInList = trim((string)$this->‪stdWrapValue('isInList', $conf));
2532  if (!‪GeneralUtility::inList($comparisonValue, $singleValueWhichNeedsToBeInList)) {
2533  $flag = false;
2534  }
2535  }
2536  if (isset($conf['bitAnd']) || isset($conf['bitAnd.'])) {
2537  $number = (int)trim((string)$this->‪stdWrapValue('bitAnd', $conf));
2538  if ((new BitSet($number))->get($comparisonValue) === false) {
2539  $flag = false;
2540  }
2541  }
2542  }
2543  if ($conf['negate'] ?? false) {
2544  $flag = !$flag;
2545  }
2546  return $flag;
2547  }
2548 
2561  public function ‪HTMLparser_TSbridge($theValue, $conf)
2562  {
2563  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
2564  $htmlParserCfg = $htmlParser->HTMLparserConfig($conf);
2565  return $htmlParser->HTMLcleaner($theValue, $htmlParserCfg[0], $htmlParserCfg[1], $htmlParserCfg[2], $htmlParserCfg[3]);
2566  }
2567 
2577  public function ‪dataWrap($content, $wrap)
2578  {
2579  return $this->‪wrap($content, $this->‪insertData($wrap));
2580  }
2597  public function ‪insertData($str)
2598  {
2599  $inside = 0;
2600  $newVal = '';
2601  $pointer = 0;
2602  $totalLen = strlen($str);
2603  do {
2604  if (!$inside) {
2605  $len = strcspn(substr($str, $pointer), '{');
2606  $newVal .= substr($str, $pointer, $len);
2607  $inside = true;
2608  if (substr($str, $pointer + $len + 1, 1) === '#') {
2609  $len2 = strcspn(substr($str, $pointer + $len), '}');
2610  $newVal .= substr($str, $pointer + $len, $len2);
2611  $len += $len2;
2612  $inside = false;
2613  }
2614  } else {
2615  $len = strcspn(substr($str, $pointer), '}') + 1;
2616  $newVal .= $this->‪getData(substr($str, $pointer + 1, $len - 2), $this->data);
2617  $inside = false;
2618  }
2619  $pointer += $len;
2620  } while ($pointer < $totalLen);
2621  return $newVal;
2622  }
2623 
2634  public function ‪prefixComment($str, $conf, $content)
2635  {
2636  if (empty($str)) {
2637  return $content;
2638  }
2639  $parts = explode('|', $str);
2640  $indent = (int)$parts[0];
2641  $comment = htmlspecialchars($this->‪insertData($parts[1]));
2642  ‪$output = LF
2643  . str_pad('', $indent, "\t") . '<!-- ' . $comment . ' [begin] -->' . LF
2644  . str_pad('', $indent + 1, "\t") . $content . LF
2645  . str_pad('', $indent, "\t") . '<!-- ' . $comment . ' [end] -->' . LF
2646  . str_pad('', $indent + 1, "\t");
2647  return ‪$output;
2648  }
2649 
2659  public function ‪substring($content, $options)
2660  {
2661  $options = ‪GeneralUtility::intExplode(',', $options . ',');
2662  if ($options[1]) {
2663  return mb_substr($content, $options[0], $options[1], 'utf-8');
2664  }
2665  return mb_substr($content, $options[0], null, 'utf-8');
2666  }
2667 
2677  public function ‪crop($content, $options)
2678  {
2679  $options = explode('|', $options);
2680  $numberOfChars = (int)$options[0];
2681  $replacementForEllipsis = trim($options[1] ?? '');
2682  $cropToSpace = trim($options[2] ?? '') === '1';
2683  return GeneralUtility::makeInstance(TextCropper::class)
2684  ->crop(
2685  content: $content,
2686  numberOfChars: $numberOfChars,
2687  replacementForEllipsis: $replacementForEllipsis,
2688  cropToSpace: $cropToSpace
2689  );
2690  }
2691 
2705  public function ‪cropHTML(string $content, string $options): string
2706  {
2707  $options = explode('|', $options);
2708  $numberOfChars = (int)$options[0];
2709  $replacementForEllipsis = trim($options[1] ?? '');
2710  $cropToSpace = trim($options[2] ?? '') === '1';
2711  return GeneralUtility::makeInstance(HtmlCropper::class)
2712  ->crop(
2713  content: $content,
2714  numberOfChars: $numberOfChars,
2715  replacementForEllipsis: $replacementForEllipsis,
2716  cropToSpace: $cropToSpace
2717  );
2718  }
2719 
2727  public function ‪calc($val)
2728  {
2729  $parts = GeneralUtility::splitCalc($val, '+-*/');
2730  $value = 0;
2731  foreach ($parts as $part) {
2732  $theVal = $part[1];
2733  $sign = $part[0];
2734  if ((string)(int)$theVal === (string)$theVal) {
2735  $theVal = (int)$theVal;
2736  } else {
2737  $theVal = 0;
2738  }
2739  if ($sign === '-') {
2740  $value -= $theVal;
2741  }
2742  if ($sign === '+') {
2743  $value += $theVal;
2744  }
2745  if ($sign === '/') {
2746  if ((int)$theVal) {
2747  $value /= (int)$theVal;
2748  }
2749  }
2750  if ($sign === '*') {
2751  $value *= $theVal;
2752  }
2753  }
2754  return $value;
2755  }
2756 
2769  public function ‪splitObj($value, $conf)
2770  {
2771  $conf['token'] = isset($conf['token.']) ? $this->‪stdWrap($conf['token'] ?? '', $conf['token.']) : $conf['token'] ?? '';
2772  if ($conf['token'] === '') {
2773  return $value;
2774  }
2775  $valArr = explode($conf['token'], $value);
2776 
2777  // return value directly by returnKey. No further processing
2778  if ($valArr !== [''] && (‪MathUtility::canBeInterpretedAsInteger($conf['returnKey'] ?? null) || ($conf['returnKey.'] ?? false))) {
2779  $key = (int)$this->‪stdWrapValue('returnKey', $conf ?? []);
2780  return $valArr[$key] ?? '';
2781  }
2782 
2783  // return the amount of elements. No further processing
2784  if ($valArr !== [''] && (($conf['returnCount'] ?? false) || ($conf['returnCount.'] ?? false))) {
2785  $returnCount = (bool)$this->‪stdWrapValue('returnCount', $conf ?? []);
2786  return $returnCount ? count($valArr) : 0;
2787  }
2788 
2789  // calculate splitCount
2790  $splitCount = count($valArr);
2791  $max = (int)$this->‪stdWrapValue('max', $conf ?? []);
2792  if ($max && $splitCount > $max) {
2793  $splitCount = $max;
2794  }
2795  $min = (int)$this->‪stdWrapValue('min', $conf ?? []);
2796  if ($min && $splitCount < $min) {
2797  $splitCount = $min;
2798  }
2799  $wrap = (string)$this->‪stdWrapValue('wrap', $conf ?? []);
2800  $cObjNumSplitConf = isset($conf['cObjNum.']) ? $this->‪stdWrap($conf['cObjNum'] ?? '', $conf['cObjNum.'] ?? []) : (string)($conf['cObjNum'] ?? '');
2801  $splitArr = [];
2802  if ($wrap !== '' || $cObjNumSplitConf !== '') {
2803  $splitArr['wrap'] = $wrap;
2804  $splitArr['cObjNum'] = $cObjNumSplitConf;
2805  $splitArr = GeneralUtility::makeInstance(TypoScriptService::class)
2806  ->explodeConfigurationForOptionSplit($splitArr, $splitCount);
2807  }
2808  $content = '';
2809  for ($a = 0; $a < $splitCount; $a++) {
2810  $this->‪getTypoScriptFrontendController()->register['SPLIT_COUNT'] = $a;
2811  $value = '' . $valArr[$a];
2812  $this->data[‪$this->currentValKey] = $value;
2813  if ($splitArr[$a]['cObjNum'] ?? false) {
2814  $objName = (int)$splitArr[$a]['cObjNum'];
2815  $value = (string)(isset($conf[$objName . '.'])
2816  ? $this->‪stdWrap($this->‪cObjGet($conf[$objName . '.'], $objName . '.'), $conf[$objName . '.'])
2817  : '');
2818  }
2819  $wrap = (string)$this->‪stdWrapValue('wrap', $splitArr[$a] ?? []);
2820  if ($wrap) {
2821  $value = $this->‪wrap($value, $wrap);
2822  }
2823  $content .= $value;
2824  }
2825  return $content;
2826  }
2827 
2835  protected function ‪replacement($content, array $configuration)
2836  {
2837  // Sorts actions in configuration by numeric index
2838  ksort($configuration, SORT_NUMERIC);
2839  foreach ($configuration as $index => $action) {
2840  // Checks whether we have a valid action and a numeric key ending with a dot ("10.")
2841  if (is_array($action) && substr($index, -1) === '.' && ‪MathUtility::canBeInterpretedAsInteger(substr($index, 0, -1))) {
2842  $content = $this->‪replacementSingle($content, $action);
2843  }
2844  }
2845  return $content;
2846  }
2847 
2855  protected function ‪replacementSingle($content, array $configuration)
2856  {
2857  if ((isset($configuration['search']) || isset($configuration['search.'])) && (isset($configuration['replace']) || isset($configuration['replace.']))) {
2858  // Gets the strings
2859  $search = (string)$this->‪stdWrapValue('search', $configuration ?? []);
2860  $replace = (string)$this->‪stdWrapValue('replace', $configuration, null);
2861 
2862  // Determines whether regular expression shall be used
2863  $useRegularExpression = (bool)$this->‪stdWrapValue('useRegExp', $configuration, false);
2864 
2865  // Determines whether replace-pattern uses option-split
2866  $useOptionSplitReplace = (bool)$this->‪stdWrapValue('useOptionSplitReplace', $configuration, false);
2867 
2868  // Performs a replacement by preg_replace()
2869  if ($useRegularExpression) {
2870  // Get separator-character which precedes the string and separates search-string from the modifiers
2871  $separator = $search[0];
2872  $startModifiers = strrpos($search, $separator);
2873  if ($separator !== false && $startModifiers > 0) {
2874  $modifiers = substr($search, $startModifiers + 1);
2875  // remove "e" (eval-modifier), which would otherwise allow to run arbitrary PHP-code
2876  $modifiers = str_replace('e', '', $modifiers);
2877  $search = substr($search, 0, $startModifiers + 1) . $modifiers;
2878  }
2879  if ($useOptionSplitReplace) {
2880  // init for replacement
2881  $splitCount = preg_match_all($search, $content, $matches);
2882  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
2883  $replaceArray = $typoScriptService->explodeConfigurationForOptionSplit([$replace], $splitCount);
2884  $replaceCount = 0;
2885 
2886  $replaceCallback = static function ($match) use ($replaceArray, $search, &$replaceCount) {
2887  $replaceCount++;
2888  return preg_replace($search, $replaceArray[$replaceCount - 1][0], $match[0]);
2889  };
2890  $content = preg_replace_callback($search, $replaceCallback, $content);
2891  } else {
2892  $content = preg_replace($search, $replace, $content);
2893  }
2894  } elseif ($useOptionSplitReplace) {
2895  // turn search-string into a preg-pattern
2896  $searchPreg = '#' . preg_quote($search, '#') . '#';
2897 
2898  // init for replacement
2899  $splitCount = preg_match_all($searchPreg, $content, $matches);
2900  $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
2901  $replaceArray = $typoScriptService->explodeConfigurationForOptionSplit([$replace], $splitCount);
2902  $replaceCount = 0;
2903 
2904  $replaceCallback = static function () use ($replaceArray, &$replaceCount) {
2905  $replaceCount++;
2906  return $replaceArray[$replaceCount - 1][0];
2907  };
2908  $content = preg_replace_callback($searchPreg, $replaceCallback, $content);
2909  } else {
2910  $content = str_replace($search, $replace, $content);
2911  }
2912  }
2913  return $content;
2914  }
2915 
2924  protected function ‪round($content, array $conf = [])
2925  {
2926  $decimals = (int)$this->‪stdWrapValue('decimals', $conf, 0);
2927  $type = $this->‪stdWrapValue('roundType', $conf);
2928  $floatVal = (float)$content;
2929  switch ($type) {
2930  case 'ceil':
2931  $content = ceil($floatVal);
2932  break;
2933  case 'floor':
2934  $content = floor($floatVal);
2935  break;
2936  case 'round':
2937 
2938  default:
2939  $content = ‪round($floatVal, $decimals);
2940  }
2941  return $content;
2942  }
2943 
2952  public function ‪numberFormat($content, $conf)
2953  {
2954  $decimals = (int)$this->‪stdWrapValue('decimals', $conf, 0);
2955  $dec_point = (string)$this->‪stdWrapValue('dec_point', $conf, '.');
2956  $thousands_sep = (string)$this->‪stdWrapValue('thousands_sep', $conf, ',');
2957  return number_format((float)$content, $decimals, $dec_point, $thousands_sep);
2958  }
2959 
2978  public function ‪parseFunc($theValue, ?array $conf, ?string $ref = null)
2979  {
2980  // Fetch / merge reference, if any
2981  if (!empty($ref)) {
2982  $temp_conf = [
2983  'parseFunc' => $ref,
2984  'parseFunc.' => $conf ?? [],
2985  ];
2986  $temp_conf = $this->‪mergeTSRef($temp_conf, 'parseFunc');
2987  $conf = $temp_conf['parseFunc.'];
2988  }
2989  if (empty($conf)) {
2990  // `parseFunc` relies on configuration, either given in `$conf` or resolved from `$ref`
2991  throw new \LogicException('Invoked ContentObjectRenderer::parseFunc without any configuration', 1641989097);
2992  }
2993  // Handle HTML sanitizer invocation
2994  $conf['htmlSanitize'] = (bool)($conf['htmlSanitize'] ?? true);
2995  // Process:
2996  if ((string)($conf['externalBlocks'] ?? '') === '') {
2997  $result = $this->‪parseFuncInternal($theValue, $conf);
2998  if ($conf['htmlSanitize']) {
2999  $result = $this->‪stdWrap_htmlSanitize($result, $conf['htmlSanitize.'] ?? []);
3000  }
3001  return $result;
3002  }
3003  $tags = strtolower(implode(',', ‪GeneralUtility::trimExplode(',', $conf['externalBlocks'])));
3004  $htmlParser = GeneralUtility::makeInstance(HtmlParser::class);
3005  $parts = $htmlParser->splitIntoBlock($tags, $theValue);
3006  foreach ($parts as $k => $v) {
3007  if ($k % 2) {
3008  // font:
3009  $tagName = strtolower($htmlParser->getFirstTagName($v));
3010  $cfg = $conf['externalBlocks.'][$tagName . '.'] ?? [];
3011  if ($cfg === []) {
3012  continue;
3013  }
3014  if (($cfg['stripNLprev'] ?? false) || ($cfg['stripNL'] ?? false)) {
3015  $parts[$k - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $parts[$k - 1]);
3016  }
3017  if (($cfg['stripNLnext'] ?? false) || ($cfg['stripNL'] ?? false)) {
3018  if (!isset($parts[$k + 1])) {
3019  $parts[$k + 1] = '';
3020  }
3021  $parts[$k + 1] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $parts[$k + 1]);
3022  }
3023  }
3024  }
3025  foreach ($parts as $k => $v) {
3026  if ($k % 2) {
3027  $tag = $htmlParser->getFirstTag($v);
3028  $tagName = strtolower($htmlParser->getFirstTagName($v));
3029  $cfg = $conf['externalBlocks.'][$tagName . '.'] ?? [];
3030  if ($cfg === []) {
3031  continue;
3032  }
3033  if ($cfg['callRecursive'] ?? false) {
3034  $parts[$k] = $this->‪parseFunc($htmlParser->removeFirstAndLastTag($v), $conf);
3035  if (!($cfg['callRecursive.']['dontWrapSelf'] ?? false)) {
3036  if ($cfg['callRecursive.']['alternativeWrap'] ?? false) {
3037  $parts[$k] = $this->‪wrap($parts[$k], $cfg['callRecursive.']['alternativeWrap']);
3038  } else {
3039  if (is_array($cfg['callRecursive.']['tagStdWrap.'] ?? false)) {
3040  $tag = $this->‪stdWrap($tag, $cfg['callRecursive.']['tagStdWrap.']);
3041  }
3042  $parts[$k] = $tag . $parts[$k] . '</' . $tagName . '>';
3043  }
3044  }
3045  } elseif ($cfg['HTMLtableCells'] ?? false) {
3046  $rowParts = $htmlParser->splitIntoBlock('tr', $parts[$k]);
3047  foreach ($rowParts as $kk => $vv) {
3048  if ($kk % 2) {
3049  $colParts = $htmlParser->splitIntoBlock('td,th', $vv);
3050  $cc = 0;
3051  foreach ($colParts as $kkk => $vvv) {
3052  if ($kkk % 2) {
3053  $cc++;
3054  $tag = $htmlParser->getFirstTag($vvv);
3055  $tagName = strtolower($htmlParser->getFirstTagName($vvv));
3056  $colParts[$kkk] = $htmlParser->removeFirstAndLastTag($vvv);
3057  if (($cfg['HTMLtableCells.'][$cc . '.']['callRecursive'] ?? false)
3058  || (!isset($cfg['HTMLtableCells.'][$cc . '.']['callRecursive']) && ($cfg['HTMLtableCells.']['default.']['callRecursive'] ?? false))) {
3059  if ($cfg['HTMLtableCells.']['addChr10BetweenParagraphs'] ?? false) {
3060  $colParts[$kkk] = str_replace(
3061  '</p><p>',
3062  '</p>' . LF . '<p>',
3063  $colParts[$kkk]
3064  );
3065  }
3066  $colParts[$kkk] = $this->‪parseFunc($colParts[$kkk], $conf);
3067  }
3068  $tagStdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.'] ?? false)
3069  ? $cfg['HTMLtableCells.'][$cc . '.']['tagStdWrap.']
3070  : ($cfg['HTMLtableCells.']['default.']['tagStdWrap.'] ?? null);
3071  if (is_array($tagStdWrap)) {
3072  $tag = $this->‪stdWrap($tag, $tagStdWrap);
3073  }
3074  $stdWrap = is_array($cfg['HTMLtableCells.'][$cc . '.']['stdWrap.'] ?? false)
3075  ? $cfg['HTMLtableCells.'][$cc . '.']['stdWrap.']
3076  : ($cfg['HTMLtableCells.']['default.']['stdWrap.'] ?? null);
3077  if (is_array($stdWrap)) {
3078  $colParts[$kkk] = $this->‪stdWrap($colParts[$kkk], $stdWrap);
3079  }
3080  $colParts[$kkk] = $tag . $colParts[$kkk] . '</' . $tagName . '>';
3081  }
3082  }
3083  $rowParts[$kk] = implode('', $colParts);
3084  }
3085  }
3086  $parts[$k] = implode('', $rowParts);
3087  }
3088  if (is_array($cfg['stdWrap.'] ?? false)) {
3089  $parts[$k] = $this->‪stdWrap($parts[$k], $cfg['stdWrap.']);
3090  }
3091  } else {
3092  $parts[$k] = $this->‪parseFuncInternal($parts[$k], $conf);
3093  }
3094  }
3095  $result = implode('', $parts);
3096  if ($conf['htmlSanitize']) {
3097  $result = $this->‪stdWrap_htmlSanitize($result, $conf['htmlSanitize.'] ?? []);
3098  }
3099  return $result;
3100  }
3101 
3110  protected function ‪parseFuncInternal($theValue, $conf)
3111  {
3112  if (!empty($conf['if.']) && !$this->‪checkIf($conf['if.'])) {
3113  return $theValue;
3114  }
3115  // Indicates that the data is from within a tag.
3116  $inside = false;
3117  // Pointer to the total string position
3118  $pointer = 0;
3119  // Loaded with the current typo-tag if any.
3120  $currentTag = null;
3121  $stripNL = 0;
3122  $contentAccum = [];
3123  $contentAccumP = 0;
3124  $allowTags = strtolower(str_replace(' ', '', $conf['allowTags'] ?? ''));
3125  $denyTags = strtolower(str_replace(' ', '', $conf['denyTags'] ?? ''));
3126  $totalLen = strlen($theValue);
3127  do {
3128  if (!$inside) {
3129  if ($currentTag === null) {
3130  // These operations should only be performed on code outside the typotags...
3131  // data: this checks that we enter tags ONLY if the first char in the tag is alphanumeric OR '/'
3132  $len_p = 0;
3133  $c = 100;
3134  do {
3135  $len = strcspn(substr($theValue, $pointer + $len_p), '<');
3136  $len_p += $len + 1;
3137  $endChar = ord(strtolower(substr($theValue, $pointer + $len_p, 1)));
3138  $c--;
3139  } while ($c > 0 && $endChar && ($endChar < 97 || $endChar > 122) && $endChar != 47);
3140  $len = $len_p - 1;
3141  } else {
3142  $len = $this->‪getContentLengthOfCurrentTag($theValue, $pointer, (string)$currentTag[0]);
3143  }
3144  // $data is the content until the next <tag-start or end is detected.
3145  // In case of a currentTag set, this would mean all data between the start- and end-tags
3146  ‪$data = substr($theValue, $pointer, $len);
3147  if (‪$data !== false) {
3148  if ($stripNL) {
3149  // If the previous tag was set to strip NewLines in the beginning of the next data-chunk.
3150  ‪$data = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', ‪$data);
3151  if (‪$data === null) {
3152  $this->logger->debug('Stripping new lines failed for "{data}"', ['data' => ‪$data]);
3153  ‪$data = '';
3154  }
3155  }
3156  // These operations should only be performed on code outside the tags...
3157  if (!is_array($currentTag)) {
3158  // Short
3159  if (isset($conf['short.']) && is_array($conf['short.'])) {
3160  $shortWords = $conf['short.'];
3161  krsort($shortWords);
3162  foreach ($shortWords as $key => $val) {
3163  if (is_string($val)) {
3164  ‪$data = str_replace($key, $val, ‪$data);
3165  }
3166  }
3167  }
3168  // stdWrap
3169  if (isset($conf['plainTextStdWrap.']) && is_array($conf['plainTextStdWrap.'])) {
3170  ‪$data = $this->‪stdWrap($data, $conf['plainTextStdWrap.']);
3171  }
3172  // userFunc
3173  if ($conf['userFunc'] ?? false) {
3174  ‪$data = $this->‪callUserFunction($conf['userFunc'], $conf['userFunc.'] ?? [], ‪$data);
3175  }
3176  }
3177  // Search for tags to process in current data and
3178  // call this method recursively if found
3179  if (str_contains(‪$data, '<') && isset($conf['tags.']) && is_array($conf['tags.'])) {
3180  // @todo probably use a DOM tree traversal for the whole stuff
3181  // This iterations basically re-processes the markup string, as
3182  // long as there are `<$tag ` or `<$tag>` "tags" found...
3183  foreach (array_keys($conf['tags.']) as $tag) {
3184  // only match tag `a` in `<a href"...">` but not in `<abbr>`
3185  if (preg_match('#<' . $tag . '[\s/>]#', ‪$data)) {
3186  ‪$data = $this->‪parseFuncInternal($data, $conf);
3187  break;
3188  }
3189  }
3190  }
3191  if (!is_array($currentTag) && ($conf['makelinks'] ?? false)) {
3192  ‪$data = $this->‪http_makelinks($data, $conf['makelinks.']['http.'] ?? []);
3193  ‪$data = $this->‪mailto_makelinks($data, $conf['makelinks.']['mailto.'] ?? []);
3194  }
3195  $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
3196  ? $contentAccum[$contentAccumP] . ‪$data
3197  : ‪$data;
3198  }
3199  $inside = true;
3200  } else {
3201  // tags
3202  $len = strcspn(substr($theValue, $pointer), '>') + 1;
3203  ‪$data = substr($theValue, $pointer, $len);
3204  if (str_ends_with(‪$data, '/>') && !str_starts_with(‪$data, '<link ')) {
3205  $tagContent = substr(‪$data, 1, -2);
3206  } else {
3207  $tagContent = substr(‪$data, 1, -1);
3208  }
3209  $tag = explode(' ', trim($tagContent), 2);
3210  $tag[0] = strtolower($tag[0]);
3211  // end tag like </li>
3212  if (str_starts_with($tag[0], '/')) {
3213  $tag[0] = substr($tag[0], 1);
3214  $tag['out'] = 1;
3215  }
3216  if ($conf['tags.'][$tag[0]] ?? false) {
3217  $treated = false;
3218  $stripNL = false;
3219  // in-tag
3220  if (!$currentTag && (!isset($tag['out']) || !$tag['out'])) {
3221  // $currentTag (array!) is the tag we are currently processing
3222  $currentTag = $tag;
3223  $contentAccumP++;
3224  $treated = true;
3225  // in-out-tag: img and other empty tags
3226  if (preg_match('/^(area|base|br|col|hr|img|input|meta|param)$/i', (string)$tag[0])) {
3227  $tag['out'] = 1;
3228  }
3229  }
3230  // out-tag
3231  if (isset($currentTag[0], $tag['out']) && $currentTag[0] === $tag[0] && $tag['out']) {
3232  $theName = $conf['tags.'][$tag[0]];
3233  $theConf = $conf['tags.'][$tag[0] . '.'];
3234  // This flag indicates, that NL- (13-10-chars) should be stripped first and last.
3235  $stripNL = (bool)($theConf['stripNL'] ?? false);
3236  // This flag indicates, that this TypoTag section should NOT be included in the nonTypoTag content.
3237  $breakOut = (bool)($theConf['breakoutTypoTagContent'] ?? false);
3238  $this->parameters = [];
3239  if (isset($currentTag[1])) {
3240  // decode HTML entities in attributes, since they're processed
3241  $params = GeneralUtility::get_tag_attributes((string)$currentTag[1], true);
3242  if (is_array($params)) {
3243  foreach ($params as $option => $val) {
3244  // contains non-encoded values
3245  $this->parameters[strtolower($option)] = $val;
3246  }
3247  }
3248  $this->parameters['allParams'] = trim((string)$currentTag[1]);
3249  }
3250  // Removes NL in the beginning and end of the tag-content AND at the end of the currentTagBuffer.
3251  // $stripNL depends on the configuration of the current tag
3252  if ($stripNL) {
3253  $contentAccum[$contentAccumP - 1] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP - 1] ?? '');
3254  $contentAccum[$contentAccumP] = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $contentAccum[$contentAccumP] ?? '');
3255  $contentAccum[$contentAccumP] = preg_replace('/' . CR . '?' . LF . '[ ]*$/', '', $contentAccum[$contentAccumP] ?? '');
3256  }
3257  $this->data[‪$this->currentValKey] = $contentAccum[$contentAccumP] ?? null;
3258  $newInput = $this->‪cObjGetSingle($theName, $theConf, '/parseFunc/.tags.' . $tag[0]);
3259  // fetch the content object
3260  $contentAccum[$contentAccumP] = $newInput;
3261  $contentAccumP++;
3262  // If the TypoTag section
3263  if (!$breakOut) {
3264  if (!isset($contentAccum[$contentAccumP - 2])) {
3265  $contentAccum[$contentAccumP - 2] = '';
3266  }
3267  $contentAccum[$contentAccumP - 2] .= ($contentAccum[$contentAccumP - 1] ?? '') . ($contentAccum[$contentAccumP] ?? '');
3268  unset($contentAccum[$contentAccumP]);
3269  unset($contentAccum[$contentAccumP - 1]);
3270  $contentAccumP -= 2;
3271  }
3272  $currentTag = null;
3273  $treated = true;
3274  }
3275  // other tags
3276  if (!$treated) {
3277  $contentAccum[$contentAccumP] .= ‪$data;
3278  }
3279  } else {
3280  // If a tag was not a typo tag, then it is just added to the content
3281  $stripNL = false;
3282  if (‪GeneralUtility::inList($allowTags, (string)$tag[0]) ||
3283  ($denyTags !== '*' && !‪GeneralUtility::inList($denyTags, (string)$tag[0]))) {
3284  $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
3285  ? $contentAccum[$contentAccumP] . ‪$data
3286  : ‪$data;
3287  } else {
3288  $contentAccum[$contentAccumP] = isset($contentAccum[$contentAccumP])
3289  ? $contentAccum[$contentAccumP] . htmlspecialchars(‪$data)
3290  : htmlspecialchars(‪$data);
3291  }
3292  }
3293  $inside = false;
3294  }
3295  $pointer += $len;
3296  } while ($pointer < $totalLen);
3297  // Parsing nonTypoTag content (all even keys):
3298  reset($contentAccum);
3299  $contentAccumCount = count($contentAccum);
3300  for ($a = 0; $a < $contentAccumCount; $a++) {
3301  if ($a % 2 != 1) {
3302  // stdWrap
3303  if (isset($conf['nonTypoTagStdWrap.']) && is_array($conf['nonTypoTagStdWrap.'])) {
3304  $contentAccum[$a] = $this->‪stdWrap((string)($contentAccum[$a] ?? ''), $conf['nonTypoTagStdWrap.']);
3305  }
3306  // userFunc
3307  if (!empty($conf['nonTypoTagUserFunc'])) {
3308  $contentAccum[$a] = $this->‪callUserFunction($conf['nonTypoTagUserFunc'], $conf['nonTypoTagUserFunc.'] ?? [], (string)($contentAccum[$a] ?? ''));
3309  }
3310  }
3311  }
3312  return implode('', $contentAccum);
3313  }
3314 
3323  public function ‪encaps_lineSplit($theValue, $conf)
3324  {
3325  if ((string)$theValue === '') {
3326  return '';
3327  }
3328  $lParts = explode(LF, $theValue);
3329 
3330  // When the last element is an empty linebreak we need to remove it, otherwise we will have a duplicate empty line.
3331  $lastPartIndex = count($lParts) - 1;
3332  if ($lParts[$lastPartIndex] === '' && trim($lParts[$lastPartIndex - 1], CR) === '') {
3333  array_pop($lParts);
3334  }
3335 
3336  $encapTags = ‪GeneralUtility::trimExplode(',', strtolower($conf['encapsTagList'] ?? ''), true);
3337  $defaultAlign = trim((string)$this->‪stdWrapValue('defaultAlign', $conf ?? []));
3338 
3339  $str_content = '';
3340  foreach ($lParts as $k => $l) {
3341  $sameBeginEnd = false;
3342  $emptyTag = false;
3343  $l = trim($l);
3344  $attrib = [];
3345  $nonWrapped = false;
3346  $tagName = '';
3347  if (isset($l[0]) && $l[0] === '<' && str_ends_with($l, '>')) {
3348  $fwParts = explode('>', substr($l, 1), 2);
3349  [$tagName] = explode(' ', $fwParts[0], 2);
3350  if (!$fwParts[1]) {
3351  if (str_ends_with($tagName, '/')) {
3352  $tagName = substr($tagName, 0, -1);
3353  }
3354  if (str_ends_with($fwParts[0], '/')) {
3355  $sameBeginEnd = true;
3356  $emptyTag = true;
3357  // decode HTML entities, they're encoded later again
3358  $attrib = GeneralUtility::get_tag_attributes('<' . substr($fwParts[0], 0, -1) . '>', true);
3359  }
3360  } else {
3361  $backParts = ‪GeneralUtility::revExplode('<', substr($fwParts[1], 0, -1), 2);
3362  // decode HTML entities, they're encoded later again
3363  $attrib = GeneralUtility::get_tag_attributes('<' . $fwParts[0] . '>', true);
3364  $str_content = $backParts[0];
3365  // Ensure that $backParts could be exploded into 2 items
3366  if (isset($backParts[1])) {
3367  $sameBeginEnd = strtolower(substr($backParts[1], 1, strlen($tagName))) === strtolower($tagName);
3368  }
3369  }
3370  }
3371  if ($sameBeginEnd && in_array(strtolower($tagName), $encapTags)) {
3372  $uTagName = strtoupper($tagName);
3373  $uTagName = strtoupper($conf['remapTag.'][$uTagName] ?? $uTagName);
3374  } else {
3375  $uTagName = strtoupper($conf['nonWrappedTag'] ?? '');
3376  // The line will be wrapped: $uTagName should not be an empty tag
3377  $emptyTag = false;
3378  $str_content = $lParts[$k];
3379  $nonWrapped = true;
3380  $attrib = [];
3381  }
3382  // Wrapping all inner-content:
3383  if (is_array($conf['innerStdWrap_all.'] ?? null)) {
3384  $str_content = (string)$this->‪stdWrap($str_content, $conf['innerStdWrap_all.']);
3385  }
3386  if ($uTagName) {
3387  // Setting common attributes
3388  if (isset($conf['addAttributes.'][$uTagName . '.']) && is_array($conf['addAttributes.'][$uTagName . '.'])) {
3389  foreach ($conf['addAttributes.'][$uTagName . '.'] as $kk => $vv) {
3390  if (!is_array($vv)) {
3391  if ((string)($conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] ?? '') === 'blank') {
3392  if ((string)($attrib[$kk] ?? '') === '') {
3393  $attrib[$kk] = $vv;
3394  }
3395  } elseif ((string)($conf['addAttributes.'][$uTagName . '.'][$kk . '.']['setOnly'] ?? '') === 'exists') {
3396  if (!isset($attrib[$kk])) {
3397  $attrib[$kk] = $vv;
3398  }
3399  } else {
3400  $attrib[$kk] = $vv;
3401  }
3402  }
3403  }
3404  }
3405  // Wrapping all inner-content:
3406  if (isset($conf['encapsLinesStdWrap.'][$uTagName . '.']) && is_array($conf['encapsLinesStdWrap.'][$uTagName . '.'])) {
3407  $str_content = (string)$this->‪stdWrap($str_content, $conf['encapsLinesStdWrap.'][$uTagName . '.']);
3408  }
3409  // Default align
3410  if ((!isset($attrib['align']) || !$attrib['align']) && $defaultAlign) {
3411  $attrib['align'] = $defaultAlign;
3412  }
3413  // implode (insecure) attributes, that's why `htmlspecialchars` is used here
3414  $params = GeneralUtility::implodeAttributes($attrib, true);
3415  if (!isset($conf['removeWrapping']) || !$conf['removeWrapping'] || ($emptyTag && $conf['removeWrapping.']['keepSingleTag'])) {
3416  $selfClosingTagList = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
3417  if ($emptyTag && in_array(strtolower($uTagName), $selfClosingTagList, true)) {
3418  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . ' />';
3419  } else {
3420  $str_content = '<' . strtolower($uTagName) . (trim($params) ? ' ' . trim($params) : '') . '>' . $str_content . '</' . strtolower($uTagName) . '>';
3421  }
3422  }
3423  }
3424  if ($nonWrapped && isset($conf['wrapNonWrappedLines']) && $conf['wrapNonWrappedLines']) {
3425  $str_content = $this->‪wrap($str_content, $conf['wrapNonWrappedLines']);
3426  }
3427  $lParts[$k] = $str_content;
3428  }
3429  return implode(LF, $lParts);
3430  }
3431 
3444  protected function ‪http_makelinks(string ‪$data, array $conf): string
3445  {
3446  $parts = [];
3447  foreach (['http://', 'https://'] as $scheme) {
3448  $textpieces = explode($scheme, ‪$data);
3449  $pieces = count($textpieces);
3450  $textstr = $textpieces[0];
3451  for ($i = 1; $i < $pieces; $i++) {
3452  $len = strcspn($textpieces[$i], chr(32) . "\t" . CRLF);
3453  if (!(trim(substr($textstr, -1)) === '' && $len)) {
3454  $textstr .= $scheme . $textpieces[$i];
3455  continue;
3456  }
3457  $lastChar = substr($textpieces[$i], $len - 1, 1);
3458  if (!preg_match('/[A-Za-z0-9\\/#_-]/', $lastChar)) {
3459  $len--;
3460  }
3461  // Included '\/' 3/12
3462  $parts[0] = substr($textpieces[$i], 0, $len);
3463  $parts[1] = substr($textpieces[$i], $len);
3464  $keep = $conf['keep'] ?? '';
3465  $linkParts = parse_url($scheme . $parts[0]);
3466  // Check if link couldn't be parsed properly
3467  if (!is_array($linkParts)) {
3468  $textstr .= $scheme . $textpieces[$i];
3469  continue;
3470  }
3471  $linktxt = '';
3472  if (str_contains($keep, 'scheme')) {
3473  $linktxt = $scheme;
3474  }
3475  $linktxt .= $linkParts['host'] ?? '';
3476  if (str_contains($keep, 'path')) {
3477  $linktxt .= ($linkParts['path'] ?? '');
3478  // Added $linkParts['query'] 3/12
3479  if (str_contains($keep, 'query') && $linkParts['query']) {
3480  $linktxt .= '?' . $linkParts['query'];
3481  } elseif (($linkParts['path'] ?? '') === '/') {
3482  $linktxt = substr($linktxt, 0, -1);
3483  }
3484  }
3485  $typolinkConfiguration = $conf;
3486  $typolinkConfiguration['parameter'] = $scheme . $parts[0];
3487  $textstr .= $this->‪typoLink($linktxt, $typolinkConfiguration) . $parts[1];
3488  }
3489  ‪$data = $textstr;
3490  }
3491  return $textstr;
3492  }
3493 
3505  protected function ‪mailto_makelinks(string ‪$data, array $conf): string
3506  {
3507  $conf = (array)$conf;
3508  $parts = [];
3509  // split by mailto logic
3510  $textpieces = explode('mailto:', ‪$data);
3511  $pieces = count($textpieces);
3512  $textstr = $textpieces[0] ?? '';
3513  for ($i = 1; $i < $pieces; $i++) {
3514  $len = strcspn($textpieces[$i], chr(32) . "\t" . CRLF);
3515  if (trim(substr($textstr, -1)) === '' && $len) {
3516  $lastChar = substr($textpieces[$i], $len - 1, 1);
3517  if (!preg_match('/[A-Za-z0-9]/', $lastChar)) {
3518  $len--;
3519  }
3520  $parts[0] = substr($textpieces[$i], 0, $len);
3521  $parts[1] = substr($textpieces[$i], $len);
3522  $linktxt = (string)preg_replace('/\\?.*/', '', $parts[0]);
3523  $typolinkConfiguration = $conf;
3524  $typolinkConfiguration['parameter'] = 'mailto:' . $parts[0];
3525  $textstr .= (string)$this->‪typoLink($linktxt, $typolinkConfiguration) . $parts[1];
3526  } else {
3527  $textstr .= 'mailto:' . $textpieces[$i];
3528  }
3529  }
3530  return $textstr;
3531  }
3532 
3558  public function ‪getImgResource($file, $fileArray)
3559  {
3560  $importedFile = null;
3561  $fileReference = null;
3562  if (empty($file) && empty($fileArray)) {
3563  return null;
3564  }
3565  if (!is_array($fileArray)) {
3566  $fileArray = (array)$fileArray;
3567  }
3568  $imageResource = null;
3569  if ($file === 'GIFBUILDER') {
3570  $gifBuilder = GeneralUtility::makeInstance(GifBuilder::class);
3571  $gifBuilder->start($fileArray, $this->data);
3572  $imageResource = $gifBuilder->gifBuild();
3573  } else {
3574  if ($file instanceof File) {
3575  $fileObject = $file;
3576  } elseif ($file instanceof FileReference) {
3577  $fileReference = $file;
3578  $fileObject = $file->getOriginalFile();
3579  } else {
3580  try {
3581  if (isset($fileArray['import.']) && $fileArray['import.']) {
3582  $importedFile = trim((string)$this->‪stdWrap('', $fileArray['import.']));
3583  if (!empty($importedFile)) {
3584  $file = $importedFile;
3585  }
3586  }
3587 
3589  $treatIdAsReference = $this->‪stdWrapValue('treatIdAsReference', $fileArray);
3590  if (!empty($treatIdAsReference)) {
3591  $fileReference = $this->‪getResourceFactory()->getFileReferenceObject((int)$file);
3592  $fileObject = $fileReference->getOriginalFile();
3593  } else {
3594  $fileObject = $this->‪getResourceFactory()->getFileObject((int)$file);
3595  }
3596  } elseif (preg_match('/^(0|[1-9][0-9]*):/', $file)) { // combined identifier
3597  $fileObject = $this->‪getResourceFactory()->retrieveFileOrFolderObject($file);
3598  } else {
3599  if ($importedFile && !empty($fileArray['import'])) {
3600  $file = $fileArray['import'] . $file;
3601  }
3602  $fileObject = $this->‪getResourceFactory()->retrieveFileOrFolderObject($file);
3603  }
3604  } catch (Exception $exception) {
3605  $this->logger->warning('The image "{file}" could not be found and won\'t be included in frontend output', [
3606  'file' => $file,
3607  'exception' => $exception,
3608  ]);
3609  return null;
3610  }
3611  }
3612  if ($fileObject instanceof File) {
3613  $processingConfiguration['width'] = $this->‪stdWrapValue('width', $fileArray);
3614  $processingConfiguration['height'] = $this->‪stdWrapValue('height', $fileArray);
3615  $processingConfiguration['fileExtension'] = $this->‪stdWrapValue('ext', $fileArray);
3616  $processingConfiguration['maxWidth'] = (int)$this->‪stdWrapValue('maxW', $fileArray);
3617  $processingConfiguration['maxHeight'] = (int)$this->‪stdWrapValue('maxH', $fileArray);
3618  $processingConfiguration['minWidth'] = (int)$this->‪stdWrapValue('minW', $fileArray);
3619  $processingConfiguration['minHeight'] = (int)$this->‪stdWrapValue('minH', $fileArray);
3620  $processingConfiguration['noScale'] = $this->‪stdWrapValue('noScale', $fileArray);
3621  $processingConfiguration['sample'] = (bool)$this->‪stdWrapValue('sample', $fileArray);
3622  $processingConfiguration['additionalParameters'] = $this->‪stdWrapValue('params', $fileArray);
3623  $processingConfiguration['frame'] = (int)$this->‪stdWrapValue('frame', $fileArray);
3624  if ($fileReference === null) {
3625  $processingConfiguration['crop'] = $this->‪getCropAreaFromFromTypoScriptSettings($fileObject, $fileArray);
3626  } else {
3627  $processingConfiguration['crop'] = $this->‪getCropAreaFromFileReference($fileReference, $fileArray);
3628  }
3629 
3630  // Possibility to cancel/force profile extraction
3631  // see $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_stripColorProfileParameters']
3632  if (isset($fileArray['stripProfile'])) {
3633  $processingConfiguration['stripProfile'] = $fileArray['stripProfile'];
3634  }
3635  // Check if we can handle this type of file for editing
3636  if ($fileObject->isImage()) {
3637  $maskArray = $fileArray['m.'] ?? false;
3638  // Must render mask images and include in hash-calculating
3639  // - otherwise we cannot be sure the filename is unique for the setup!
3640  if (is_array($maskArray)) {
3641  $processingConfiguration['maskImages']['maskImage'] = $this->‪getImgResource($maskArray['mask'] ?? '', $maskArray['mask.'] ?? [])?->getProcessedFile();
3642  $processingConfiguration['maskImages']['backgroundImage'] = $this->‪getImgResource($maskArray['bgImg'] ?? '', $maskArray['bgImg.'] ?? [])?->getProcessedFile();
3643  $processingConfiguration['maskImages']['maskBottomImage'] = $this->‪getImgResource($maskArray['bottomImg'] ?? '', $maskArray['bottomImg.'] ?? [])?->getProcessedFile();
3644  $processingConfiguration['maskImages']['maskBottomImageMask'] = $this->‪getImgResource($maskArray['bottomImg_mask'] ?? '', $maskArray['bottomImg_mask.'] ?? [])?->getProcessedFile();
3645  }
3646  $processedFileObject = $fileObject->process(‪ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $processingConfiguration);
3647  if ($processedFileObject->isProcessed()) {
3648  $imageResource = ImageResource::createFromProcessedFile($processedFileObject);
3649  }
3650  }
3651  }
3652  }
3653 
3654  return GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
3655  new AfterImageResourceResolvedEvent($file, $fileArray, $imageResource)
3656  )->getImageResource();
3657  }
3658 
3676  protected function ‪getCropAreaFromFileReference(FileReference $fileReference, array $fileArray)
3677  {
3678  // Use cropping area from file reference if nothing is configured in TypoScript.
3679  if (!isset($fileArray['crop']) && !isset($fileArray['crop.'])) {
3680  // Set crop variant from TypoScript settings. If not set, use default.
3681  $cropVariant = $fileArray['cropVariant'] ?? 'default';
3682  $fileCropArea = $this->‪createCropAreaFromJsonString((string)$fileReference->getProperty('crop'), $cropVariant);
3683  return $fileCropArea->isEmpty() ? null : $fileCropArea->makeAbsoluteBasedOnFile($fileReference);
3684  }
3685 
3686  return $this->‪getCropAreaFromFromTypoScriptSettings($fileReference, $fileArray);
3687  }
3688 
3695  protected function ‪getCropAreaFromFromTypoScriptSettings(FileInterface $file, array $fileArray)
3696  {
3698  $cropArea = null;
3699  // Resolve TypoScript configured cropping.
3700  $cropSettings = isset($fileArray['crop.'])
3701  ? $this->‪stdWrap($fileArray['crop'] ?? '', $fileArray['crop.'])
3702  : ($fileArray['crop'] ?? null);
3703 
3704  if (is_string($cropSettings)) {
3705  // Set crop variant from TypoScript settings. If not set, use default.
3706  $cropVariant = $fileArray['cropVariant'] ?? 'default';
3707  // Get cropArea from CropVariantCollection, if cropSettings is a valid json.
3708  // CropVariantCollection::create does json_decode.
3709  $jsonCropArea = $this->‪createCropAreaFromJsonString($cropSettings, $cropVariant);
3710  $cropArea = $jsonCropArea->isEmpty() ? null : $jsonCropArea->makeAbsoluteBasedOnFile($file);
3711 
3712  // Cropping is configured in TypoScript in the following way: file.crop = 50,50,100,100
3713  if ($jsonCropArea->isEmpty() && preg_match('/^[0-9]+,[0-9]+,[0-9]+,[0-9]+$/', $cropSettings)) {
3714  $cropSettings = explode(',', $cropSettings);
3715  if (count($cropSettings) === 4) {
3716  $cropSettings = array_map(floatval(...), $cropSettings);
3717  $stringCropArea = GeneralUtility::makeInstance(
3718  Area::class,
3719  ...$cropSettings
3720  );
3721  $cropArea = $stringCropArea->isEmpty() ? null : $stringCropArea;
3722  }
3723  }
3724  }
3725 
3726  return $cropArea;
3727  }
3728 
3733  protected function ‪createCropAreaFromJsonString(string $cropSettings, string $cropVariant): ‪Area
3734  {
3735  return ‪CropVariantCollection::create($cropSettings)->getCropArea($cropVariant);
3736  }
3737 
3738  /***********************************************
3739  *
3740  * Data retrieval etc.
3741  *
3742  ***********************************************/
3749  public function ‪getFieldVal($field)
3750  {
3751  if (!str_contains($field, '//')) {
3752  return $this->data[trim($field)] ?? null;
3753  }
3754  $sections = ‪GeneralUtility::trimExplode('//', $field, true);
3755  foreach ($sections as $k) {
3756  if ((string)($this->data[$k] ?? '') !== '') {
3757  return $this->data[$k];
3758  }
3759  }
3760 
3761  return '';
3762  }
3763 
3775  public function ‪getData($string, $fieldArray = null)
3776  {
3777  if (!is_array($fieldArray)) {
3778  $fieldArray = $this->‪getRequest()->getAttribute('frontend.page.information')->getPageRecord();
3779  }
3780  $retVal = '';
3781  // @todo: getData should not be called with non-string as $string. example trigger:
3782  // SecureHtmlRenderingTest htmlViewHelperAvoidsCrossSiteScripting set #07 PHP 8
3783  $sections = is_string($string) ? explode('//', $string) : [];
3784  foreach ($sections as $secVal) {
3785  if ($retVal) {
3786  break;
3787  }
3788  $parts = explode(':', $secVal, 2);
3789  $type = strtolower(trim($parts[0]));
3790  $typesWithOutParameters = ['level', 'date', 'current', 'pagelayout', 'applicationcontext'];
3791  $key = trim($parts[1] ?? '');
3792  if (($key != '') || in_array($type, $typesWithOutParameters)) {
3793  switch ($type) {
3794  case 'gp':
3795  // Merge GET and POST and get $key out of the merged array
3796  $requestParameters = $this->‪getRequest()->getQueryParams();
3797  $requestParameters = array_replace_recursive($requestParameters, (array)$this->‪getRequest()->getParsedBody());
3798  $retVal = $this->‪getGlobal($key, $requestParameters);
3799  break;
3800  case 'request':
3801  $retVal = $this->‪getValueFromRecursiveData(‪GeneralUtility::trimExplode('|', $key), $this->‪getRequest());
3802  break;
3803  case 'tsfe':
3804  // @todo: This needs a bigger cleanup / deprecation when TypoScriptFrontendController continues to remove properties.
3805  $valueParts = ‪GeneralUtility::trimExplode('|', $key);
3806  if (($valueParts[0] ?? '') === 'fe_user') {
3807  $frontendUser = $this->‪getRequest()->getAttribute('frontend.user');
3808  array_shift($valueParts);
3809  $retVal = $this->‪getValueFromRecursiveData($valueParts, $frontendUser);
3810  } elseif (($valueParts[0] ?? '') === 'linkVars') {
3811  $typoScriptConfigArray = $this->‪getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
3812  $typoScriptConfigLinkVars = (string)($typoScriptConfigArray['linkVars'] ?? '');
3813  $retVal = GeneralUtility::makeInstance(LinkVarsCalculator::class)
3814  ->getAllowedLinkVarsFromRequest(
3815  $typoScriptConfigLinkVars,
3816  $this->‪getRequest()->getQueryParams(),
3817  GeneralUtility::makeInstance(Context::class)
3818  );
3819  } else {
3820  $retVal = $this->‪getValueFromRecursiveData($valueParts, $this->‪getTypoScriptFrontendController());
3821  }
3822  break;
3823  case 'getenv':
3824  $retVal = getenv($key);
3825  break;
3826  case 'getindpenv':
3827  $retVal = $this->‪getEnvironmentVariable($key);
3828  break;
3829  case 'field':
3830  $retVal = $this->‪getGlobal($key, $fieldArray);
3831  break;
3832  case 'file':
3833  $retVal = $this->‪getFileDataKey($key);
3834  break;
3835  case 'parameters':
3836  $retVal = $this->parameters[$key] ?? null;
3837  break;
3838  case 'register':
3839  $tsfe = $this->‪getTypoScriptFrontendController();
3840  $retVal = $tsfe->register[$key] ?? null;
3841  break;
3842  case 'global':
3843  $retVal = $this->‪getGlobal($key);
3844  break;
3845  case 'level':
3846  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3847  $retVal = count($localRootLine) - 1;
3848  break;
3849  case 'leveltitle':
3850  $keyParts = ‪GeneralUtility::trimExplode(',', $key);
3851  $pointer = (int)($keyParts[0] ?? 0);
3852  $slide = $keyParts[1] ?? '';
3853  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3854  $numericKey = $this->‪getKey($pointer, $localRootLine);
3855  $retVal = $this->‪rootLineValue($numericKey, 'title', strtolower($slide) === 'slide');
3856  break;
3857  case 'levelmedia':
3858  $keyParts = ‪GeneralUtility::trimExplode(',', $key);
3859  $pointer = (int)($keyParts[0] ?? 0);
3860  $slide = $keyParts[1] ?? '';
3861  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3862  $numericKey = $this->‪getKey($pointer, $localRootLine);
3863  $retVal = $this->‪rootLineValue($numericKey, 'media', strtolower($slide) === 'slide');
3864  break;
3865  case 'leveluid':
3866  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3867  $numericKey = $this->‪getKey((int)$key, $localRootLine);
3868  $retVal = $this->‪rootLineValue($numericKey, 'uid');
3869  break;
3870  case 'levelfield':
3871  $keyParts = ‪GeneralUtility::trimExplode(',', $key);
3872  $pointer = (int)($keyParts[0] ?? 0);
3873  $field = $keyParts[1] ?? '';
3874  $slide = $keyParts[2] ?? '';
3875  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3876  $numericKey = $this->‪getKey($pointer, $localRootLine);
3877  $retVal = $this->‪rootLineValue($numericKey, $field, strtolower($slide) === 'slide');
3878  break;
3879  case 'fullrootline':
3880  $keyParts = ‪GeneralUtility::trimExplode(',', $key);
3881  $pointer = (int)($keyParts[0] ?? 0);
3882  $field = $keyParts[1] ?? '';
3883  $slide = $keyParts[2] ?? '';
3884  $rootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getRootLine();
3885  $localRootLine = $this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
3886  $fullKey = $pointer - count($localRootLine) + count($rootLine);
3887  if ($fullKey >= 0) {
3888  $retVal = $this->‪rootLineValue($fullKey, $field, stristr($slide, 'slide') !== false, $rootLine);
3889  }
3890  break;
3891  case 'date':
3892  if (!$key) {
3893  $key = 'd/m Y';
3894  }
3895  $retVal = date($key, ‪$GLOBALS['EXEC_TIME']);
3896  break;
3897  case 'page':
3898  $pageRecord = $this->‪getRequest()->getAttribute('frontend.page.information')->getPageRecord();
3899  $retVal = $pageRecord[$key] ?? '';
3900  break;
3901  case 'pagelayout':
3902  $pageInformation = $this->‪getRequest()->getAttribute('frontend.page.information');
3903  $pageLayoutResolver = GeneralUtility::makeInstance(PageLayoutResolver::class);
3904  $retVal = $pageLayoutResolver->getLayoutIdentifierForPage($pageInformation->getPageRecord(), $pageInformation->getRootLine());
3905  break;
3906  case 'current':
3907  $retVal = $this->data[‪$this->currentValKey] ?? null;
3908  break;
3909  case 'db':
3910  $selectParts = ‪GeneralUtility::trimExplode(':', $key, true);
3911  if (!isset($selectParts[1])) {
3912  break;
3913  }
3914  $pageRepository = $this->‪getPageRepository();
3915  $dbRecord = $pageRepository->getRawRecord($selectParts[0], (int)$selectParts[1]);
3916  if (is_array($dbRecord) && isset($selectParts[2])) {
3917  $retVal = $dbRecord[$selectParts[2]] ?? '';
3918  }
3919  break;
3920  case 'lll':
3921  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
3922  $language = $this->‪getRequest()->getAttribute('language') ?? $this->‪getRequest()->getAttribute('site')->getDefaultLanguage();
3923  $languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->createFromSiteLanguage($language);
3924  $retVal = $languageService->sL('LLL:' . $key);
3925  break;
3926  case 'path':
3927  try {
3928  $retVal = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($key);
3929  } catch (Exception) {
3930  // do nothing in case the file path is invalid
3931  $retVal = null;
3932  }
3933  break;
3934  case 'cobj':
3935  switch ($key) {
3936  case 'parentRecordNumber':
3937  $retVal = ‪$this->parentRecordNumber;
3938  break;
3939  }
3940  break;
3941  case 'debug':
3942  switch ($key) {
3943  case 'rootLine':
3944  $retVal = ‪DebugUtility::viewArray($this->‪getRequest()->getAttribute('frontend.page.information')->getLocalRootLine());
3945  break;
3946  case 'fullRootLine':
3947  $retVal = ‪DebugUtility::viewArray($this->‪getRequest()->getAttribute('frontend.page.information')->getRootLine());
3948  break;
3949  case 'data':
3950  $retVal = ‪DebugUtility::viewArray($this->data);
3951  break;
3952  case 'register':
3953  $tsfe = $this->‪getTypoScriptFrontendController();
3954  $retVal = ‪DebugUtility::viewArray($tsfe->register);
3955  break;
3956  case 'page':
3957  $retVal = ‪DebugUtility::viewArray($this->‪getRequest()->getAttribute('frontend.page.information')->getPageRecord());
3958  break;
3959  }
3960  break;
3961  case 'flexform':
3962  $keyParts = ‪GeneralUtility::trimExplode(':', $key, true);
3963  if (count($keyParts) === 2 && isset($this->data[$keyParts[0]])) {
3964  $flexFormContent = $this->data[$keyParts[0]];
3965  if (!empty($flexFormContent)) {
3966  $flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
3967  $flexFormKey = str_replace('.', '|', $keyParts[1]);
3968  $settings = $flexFormService->convertFlexFormContentToArray($flexFormContent);
3969  $retVal = $this->‪getGlobal($flexFormKey, $settings);
3970  }
3971  }
3972  break;
3973  case 'session':
3974  $keyParts = ‪GeneralUtility::trimExplode('|', $key, true);
3975  $sessionKey = array_shift($keyParts);
3976  $retVal = $this->‪getRequest()->getAttribute('frontend.user')->getSessionData($sessionKey);
3977  foreach ($keyParts as $keyPart) {
3978  if (is_object($retVal)) {
3979  $retVal = $retVal->{$keyPart};
3980  } elseif (is_array($retVal)) {
3981  $retVal = $retVal[$keyPart];
3982  } else {
3983  $retVal = '';
3984  break;
3985  }
3986  }
3987  if (!is_scalar($retVal)) {
3988  $retVal = '';
3989  }
3990  break;
3991  case 'context':
3992  $context = GeneralUtility::makeInstance(Context::class);
3993  [$aspectName, $propertyName] = ‪GeneralUtility::trimExplode(':', $key, true, 2);
3994  $retVal = $context->getPropertyFromAspect($aspectName, $propertyName, '');
3995  if (is_array($retVal)) {
3996  $retVal = implode(',', $retVal);
3997  }
3998  if (!is_scalar($retVal)) {
3999  $retVal = '';
4000  }
4001  break;
4002  case 'site':
4003  $site = $this->‪getRequest()->getAttribute('site');
4004  if ($key === 'identifier') {
4005  $retVal = $site->getIdentifier();
4006  } elseif ($key === 'base') {
4007  $retVal = $site->getBase();
4008  } else {
4009  try {
4010  $retVal = ‪ArrayUtility::getValueByPath($site->getConfiguration(), $key, '.');
4011  } catch (MissingArrayPathException $exception) {
4012  $this->logger->notice('Configuration "{key}" is not defined for site "{site}"', ['key' => $key, 'site' => $site->getIdentifier(), 'exception' => $exception]);
4013  }
4014  }
4015  break;
4016  case 'sitelanguage':
4017  // @todo: Check when/if there are scenarios where attribute 'language' is not yet set in $request.
4018  $siteLanguage = $this->‪getRequest()->getAttribute('language') ?? $this->‪getRequest()->getAttribute('site')->getDefaultLanguage();
4019  if ($key === 'twoLetterIsoCode') {
4020  $key = 'locale:languageCode';
4021  }
4022  if ($key === 'hreflang') {
4023  $key = 'locale:full';
4024  }
4025  // Harmonizing the namings from the site configuration value with the TypoScript setting
4026  if ($key === 'flag') {
4027  $key = 'flagIdentifier';
4028  }
4029  // Special handling for the locale object
4030  if (str_starts_with($key, 'locale')) {
4031  $localeObject = $siteLanguage->getLocale();
4032  if ($key === 'locale') {
4033  // backwards-compatibility
4034  $retVal = $localeObject->posixFormatted();
4035  } else {
4036  $keyParts = explode(':', $key, 2);
4037  switch ($keyParts[1] ?? '') {
4038  case 'languageCode':
4039  $retVal = $localeObject->getLanguageCode();
4040  break;
4041  case 'countryCode':
4042  $retVal = $localeObject->getCountryCode();
4043  break;
4044  case 'full':
4045  default:
4046  $retVal = $localeObject->getName();
4047  }
4048  }
4049  } else {
4050  $config = $siteLanguage->toArray();
4051  if (isset($config[$key])) {
4052  $retVal = $config[$key] ?? '';
4053  }
4054  }
4055  break;
4056  case 'sitesettings':
4057  $siteSettings = $this->‪getRequest()->getAttribute('site')->getSettings();
4058  $retVal = $siteSettings->get($key, '');
4059  break;
4060  case 'applicationcontext':
4061  $retVal = ‪Environment::getContext()->__toString();
4062  break;
4063  }
4064  }
4065  }
4066 
4067  return GeneralUtility::makeInstance(EventDispatcherInterface::class)->dispatch(
4068  new ‪AfterGetDataResolvedEvent($string, $fieldArray, $retVal, $this)
4069  )->getResult();
4070  }
4071 
4081  protected function getFileDataKey($key)
4082  {
4083  [$fileUidOrCurrentKeyword, $requestedFileInformationKey] = ‪GeneralUtility::trimExplode(':', $key, false, 3);
4084  try {
4085  if ($fileUidOrCurrentKeyword === 'current') {
4086  $fileObject = $this->getCurrentFile();
4087  } elseif (‪MathUtility::canBeInterpretedAsInteger($fileUidOrCurrentKeyword)) {
4088  $fileFactory = GeneralUtility::makeInstance(ResourceFactory::class);
4089  $fileObject = $fileFactory->getFileObject((int)$fileUidOrCurrentKeyword);
4090  } else {
4091  $fileObject = null;
4092  }
4093  } catch (Exception $exception) {
4094  $this->logger->warning('The file "{uid}" could not be found and won\'t be included in frontend output', ['uid' => $fileUidOrCurrentKeyword, 'exception' => $exception]);
4095  $fileObject = null;
4096  }
4097 
4098  if ($fileObject instanceof FileInterface) {
4099  // All properties of the \TYPO3\CMS\Core\Resource\FileInterface are available here:
4100  switch ($requestedFileInformationKey) {
4101  case 'name':
4102  return $fileObject->getName();
4103  case 'uid':
4104  if (method_exists($fileObject, 'getUid')) {
4105  return $fileObject->getUid();
4106  }
4107  return 0;
4108  case 'originalUid':
4109  if ($fileObject instanceof FileReference) {
4110  return $fileObject->getOriginalFile()->getUid();
4111  }
4112  return null;
4113  case 'size':
4114  return $fileObject->getSize();
4115  case 'sha1':
4116  return $fileObject->getSha1();
4117  case 'extension':
4118  return $fileObject->getExtension();
4119  case 'mimetype':
4120  return $fileObject->getMimeType();
4121  case 'contents':
4122  return $fileObject->getContents();
4123  case 'publicUrl':
4124  return $fileObject->getPublicUrl();
4125  default:
4126  // Generic alternative here
4127  return $fileObject->getProperty($requestedFileInformationKey);
4128  }
4129  } else {
4130  // @todo fail silently as is common in tslib_content
4131  return 'Error: no file object';
4132  }
4133  }
4134 
4146  protected function rootLineValue($key, $field, $slideBack = false, $altRootLine = ''): string
4147  {
4148  if (is_array($altRootLine)) {
4149  $rootLine = $altRootLine;
4150  } else {
4151  $rootLine = $this->getRequest()->getAttribute('frontend.page.information')->getLocalRootLine();
4152  }
4153  if (!$slideBack) {
4154  return $rootLine[$key][$field] ?? '';
4155  }
4156  for ($a = $key; $a >= 0; $a--) {
4157  $val = $rootLine[$a][$field] ?? '';
4158  if ($val) {
4159  return $val;
4160  }
4161  }
4162  return '';
4163  }
4164 
4174  public function getGlobal($keyString, $source = null)
4175  {
4176  $keys = ‪GeneralUtility::trimExplode('|', $keyString);
4177  // remove the first key, as this is only used for finding the original value
4178  $rootKey = array_shift($keys);
4179  $value = isset($source) ? ($source[$rootKey] ?? '') : (‪$GLOBALS[$rootKey] ?? '');
4180  return $this->getValueFromRecursiveData($keys, $value);
4181  }
4182 
4189  protected function getValueFromRecursiveData(array $keys, mixed $startValue): int|float|string
4190  {
4191  $value = $startValue;
4192  $numberOfLevels = count($keys);
4193  for ($i = 0; $i < $numberOfLevels && isset($value); $i++) {
4194  $currentKey = $keys[$i];
4195  if (is_object($value)) {
4196  // getter method
4197  if (method_exists($value, 'get' . ucfirst($currentKey))) {
4198  $getterMethod = 'get' . ucfirst($currentKey);
4199  $value = $value->$getterMethod(...)();
4200  // server request attribute, such as "routing"
4201  } elseif ($value instanceof ServerRequestInterface) {
4202  $value = $value->getAttribute($currentKey);
4203  } else {
4204  // Public property
4205  $value = $value->{$currentKey};
4206  }
4207  } elseif (is_array($value)) {
4208  $value = $value[$currentKey] ?? '';
4209  } else {
4210  $value = '';
4211  break;
4212  }
4213  }
4214  if (!is_scalar($value)) {
4215  $value = '';
4216  }
4217  return $value;
4218  }
4219 
4230  public function getKey($key, $arr)
4231  {
4232  $key = (int)$key;
4233  if (is_array($arr)) {
4234  if ($key < 0) {
4235  $key = count($arr) + $key;
4236  }
4237  if ($key < 0) {
4238  $key = 0;
4239  }
4240  }
4241  return $key;
4242  }
4243 
4244  /***********************************************
4245  *
4246  * Link functions (typolink)
4247  *
4248  ***********************************************/
4249 
4268  public function typoLink(string $linkText, array $conf)
4269  {
4270  try {
4271  $linkResult = $this->createLink($linkText, $conf);
4272  } catch (UnableToLinkException $e) {
4273  return $e->getLinkText();
4274  }
4275 
4276  // If flag "returnLast" set, then just return the latest URL / url / target that was built.
4277  // This returns the information without being wrapped in a "LinkResult" object.
4278  switch ($conf['returnLast'] ?? null) {
4279  case 'url':
4280  return $linkResult->getUrl();
4281  case 'target':
4282  return $linkResult->getTarget();
4283  case 'result':
4284  // kept for backwards-compatibility, as this was added in TYPO3 v11
4285  return LinkResult::adapt($linkResult, LinkResult::STRING_CAST_JSON);
4286  }
4287 
4288  $wrap = (string)$this->stdWrapValue('wrap', $conf);
4289  if ($conf['ATagBeforeWrap'] ?? false) {
4290  $linkResult = $linkResult->withLinkText($this->wrap((string)$linkResult->getLinkText(), $wrap));
4291  return LinkResult::adapt($linkResult)->getHtml();
4292  }
4293  $result = LinkResult::adapt($linkResult)->getHtml();
4294  return $this->wrap($result, $wrap);
4295  }
4296 
4314  public function createLink(string $linkText, array $conf): LinkResultInterface
4315  {
4316  $this->lastTypoLinkResult = null;
4317  try {
4318  $linkResult = GeneralUtility::makeInstance(LinkFactory::class)->create($linkText, $conf, $this);
4319  } catch (UnableToLinkException $e) {
4320  // URL could not be generated
4321  throw $e;
4322  }
4323 
4324  $this->lastTypoLinkResult = $linkResult;
4325  return $linkResult;
4326  }
4327 
4337  public function createUrl(array $conf): string
4338  {
4339  try {
4340  return $this->createLink('', $conf)->getUrl();
4341  } catch (‪UnableToLinkException $e) {
4342  // URL could not be generated
4343  return '';
4344  }
4345  }
4346 
4354  public function typoLink_URL($conf)
4355  {
4356  return $this->createUrl($conf ?? []);
4357  }
4358 
4359  /***********************************************
4360  *
4361  * Miscellaneous functions, stand alone
4362  *
4363  ***********************************************/
4375  public function wrap($content, $wrap, $char = '|')
4376  {
4377  if ($wrap) {
4378  $wrapArr = explode($char, $wrap);
4379  $content = trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
4380  }
4381  return $content;
4382  }
4383 
4394  public function noTrimWrap($content, $wrap, $char = '|')
4395  {
4396  if ($wrap) {
4397  // expects to be wrapped with (at least) 3 characters (before, middle, after)
4398  // anything else is not taken into account
4399  $wrapArr = explode($char, $wrap, 4);
4400  $content = ($wrapArr[1] ?? '') . $content . ($wrapArr[2] ?? '');
4401  }
4402  return $content;
4403  }
4404 
4414  public function callUserFunction($funcName, $conf, $content)
4415  {
4416  // Split parts
4417  $parts = explode('->', $funcName);
4418  if (count($parts) === 2) {
4419  // Check whether PHP class is available
4420  if (class_exists($parts[0])) {
4421  if ($this->container && $this->container->has($parts[0])) {
4422  $classObj = $this->container->get($parts[0]);
4423  } else {
4424  $classObj = GeneralUtility::makeInstance($parts[0]);
4425  }
4426  $methodName = (string)$parts[1];
4427  $callable = [$classObj, $methodName];
4428 
4429  if (is_object($classObj) && method_exists($classObj, $parts[1]) && is_callable($callable)) {
4430  if (method_exists($classObj, 'setContentObjectRenderer') && is_callable([$classObj, 'setContentObjectRenderer'])) {
4431  $classObj->setContentObjectRenderer($this);
4432  }
4433  $content = $callable($content, $conf, $this->getRequest()->withAttribute('currentContentObject', $this));
4434  } else {
4435  $this->getTimeTracker()->setTSlogMessage('Method "' . $parts[1] . '" did not exist in class "' . $parts[0] . '"', LogLevel::ERROR);
4436  }
4437  } else {
4438  $this->getTimeTracker()->setTSlogMessage('Class "' . $parts[0] . '" did not exist', LogLevel::ERROR);
4439  }
4440  } elseif (function_exists($funcName)) {
4441  $content = $funcName($content, $conf, $this->getRequest()->withAttribute('currentContentObject', $this));
4442  } else {
4443  $this->getTimeTracker()->setTSlogMessage('Function "' . $funcName . '" did not exist', LogLevel::ERROR);
4444  }
4445  return $content;
4446  }
4447 
4454  public function keywords($content)
4455  {
4456  $listArr = preg_split('/[,;' . LF . ']/', $content);
4457  if ($listArr === false) {
4458  return '';
4459  }
4460  foreach ($listArr as $k => $v) {
4461  $listArr[$k] = trim($v);
4462  }
4463  return implode(',', $listArr);
4464  }
4465 
4474  public function caseshift($theValue, $case)
4475  {
4476  switch (strtolower($case)) {
4477  case 'upper':
4478  $theValue = mb_strtoupper($theValue, 'utf-8');
4479  break;
4480  case 'lower':
4481  $theValue = mb_strtolower($theValue, 'utf-8');
4482  break;
4483  case 'capitalize':
4484  $theValue = mb_convert_case($theValue, MB_CASE_TITLE, 'utf-8');
4485  break;
4486  case 'ucfirst':
4487  $firstChar = mb_substr($theValue, 0, 1, 'utf-8');
4488  $firstChar = mb_strtoupper($firstChar, 'utf-8');
4489  $remainder = mb_substr($theValue, 1, null, 'utf-8');
4490  $theValue = $firstChar . $remainder;
4491  break;
4492  case 'lcfirst':
4493  $firstChar = mb_substr($theValue, 0, 1, 'utf-8');
4494  $firstChar = mb_strtolower($firstChar, 'utf-8');
4495  $remainder = mb_substr($theValue, 1, null, 'utf-8');
4496  $theValue = $firstChar . $remainder;
4497  break;
4498  case 'uppercamelcase':
4499  $theValue = GeneralUtility::underscoredToUpperCamelCase($theValue);
4500  break;
4501  case 'lowercamelcase':
4502  $theValue = GeneralUtility::underscoredToLowerCamelCase($theValue);
4503  break;
4504  }
4505  return $theValue;
4506  }
4507 
4516  public function HTMLcaseshift($theValue, $case)
4517  {
4518  $inside = 0;
4519  $newVal = '';
4520  $pointer = 0;
4521  $totalLen = strlen($theValue);
4522  do {
4523  if (!$inside) {
4524  $len = strcspn(substr($theValue, $pointer), '<');
4525  $newVal .= $this->caseshift(substr($theValue, $pointer, $len), $case);
4526  $inside = 1;
4527  } else {
4528  $len = strcspn(substr($theValue, $pointer), '>') + 1;
4529  $newVal .= substr($theValue, $pointer, $len);
4530  $inside = 0;
4531  }
4532  $pointer += $len;
4533  } while ($pointer < $totalLen);
4534  return $newVal;
4535  }
4536 
4544  public function calcAge($seconds, $labels = null)
4545  {
4546  if ($labels === null || MathUtility::canBeInterpretedAsInteger($labels)) {
4547  $labels = ' min| hrs| days| yrs| min| hour| day| year';
4548  } else {
4549  $labels = str_replace('"', '', $labels);
4550  }
4551  $labelArr = explode('|', $labels);
4552  if (count($labelArr) === 4) {
4553  $labelArr = array_merge($labelArr, $labelArr);
4554  }
4555  $absSeconds = abs($seconds);
4556  $sign = $seconds > 0 ? 1 : -1;
4557  if ($absSeconds < 3600) {
4558  $val = round($absSeconds / 60);
4559  $seconds = $sign * $val . ($val == 1 ? $labelArr[4] : $labelArr[0]);
4560  } elseif ($absSeconds < 24 * 3600) {
4561  $val = round($absSeconds / 3600);
4562  $seconds = $sign * $val . ($val == 1 ? $labelArr[5] : $labelArr[1]);
4563  } elseif ($absSeconds < 365 * 24 * 3600) {
4564  $val = round($absSeconds / (24 * 3600));
4565  $seconds = $sign * $val . ($val == 1 ? $labelArr[6] : $labelArr[2]);
4566  } else {
4567  $val = round($absSeconds / (365 * 24 * 3600));
4568  $seconds = $sign * $val . ($val == 1 ? ($labelArr[7] ?? null) : ($labelArr[3] ?? null));
4569  }
4570  return $seconds;
4571  }
4572 
4591  public function mergeTSRef(array $typoScriptArray, string $propertyName): array
4592  {
4593  if (!isset($typoScriptArray[$propertyName]) || !str_starts_with($typoScriptArray[$propertyName], '<')) {
4594  return $typoScriptArray;
4595  }
4596  $frontendTypoScript = $this->getRequest()->getAttribute('frontend.typoscript');
4597  if (!$frontendTypoScript || !$frontendTypoScript->hasSetup()) {
4598  return $typoScriptArray;
4599  }
4600  $fullTypoScriptArray = $frontendTypoScript->getSetupArray();
4601  $dottedSourceIdentifier = trim(substr($typoScriptArray[$propertyName], 1));
4602  $dottedSourceIdentifierArray = StringUtility::explodeEscaped('.', $dottedSourceIdentifier);
4603  $overrideConfig = $typoScriptArray[$propertyName . '.'] ?? [];
4604  $resolvedValue = $dottedSourceIdentifier;
4605  $resolvedConfig = $fullTypoScriptArray;
4606  foreach ($dottedSourceIdentifierArray as $identifierPart) {
4607  if (!isset($resolvedConfig[$identifierPart . '.'])) {
4608  $resolvedValue = $dottedSourceIdentifier;
4609  $resolvedConfig = $overrideConfig;
4610  break;
4611  }
4612  $resolvedValue = $resolvedConfig[$identifierPart] ?? $resolvedValue;
4613  $resolvedConfig = $resolvedConfig[$identifierPart . '.'];
4614  }
4615  $resolvedConfig = array_replace_recursive($resolvedConfig, $overrideConfig);
4616  $typoScriptArray[$propertyName] = $resolvedValue;
4617  $typoScriptArray[$propertyName . '.'] = $resolvedConfig;
4618  if (!isset($typoScriptArray[$propertyName]) || !str_starts_with($typoScriptArray[$propertyName], '<')) {
4619  return $typoScriptArray;
4620  }
4621  // Call recursive to resolve a nested =< operator
4622  return $this->mergeTSRef($typoScriptArray, $propertyName);
4623  }
4625  /***********************************************
4626  *
4627  * Database functions, making of queries
4628  *
4629  ***********************************************/
4630 
4640  public function searchWhere($searchWords, $searchFieldList, $searchTable)
4641  {
4642  if (!$searchWords) {
4643  return '';
4644  }
4645 
4646  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
4647  ->getQueryBuilderForTable($searchTable);
4648 
4649  $prefixTableName = $searchTable ? $searchTable . '.' : '';
4650 
4651  $where = $queryBuilder->expr()->and();
4652  $searchFields = explode(',', $searchFieldList);
4653  $searchWords = preg_split('/[ ,]/', $searchWords);
4654  foreach ($searchWords as $searchWord) {
4655  $searchWord = trim($searchWord);
4656  if (strlen($searchWord) < 3) {
4657  continue;
4658  }
4659  $searchWordConstraint = $queryBuilder->expr()->or();
4660  $searchWord = $queryBuilder->escapeLikeWildcards($searchWord);
4661  foreach ($searchFields as $field) {
4662  $searchWordConstraint = $searchWordConstraint->with(
4663  $queryBuilder->expr()->like($prefixTableName . $field, $queryBuilder->quote('%' . $searchWord . '%'))
4664  );
4665  }
4666 
4667  if ($searchWordConstraint->count()) {
4668  $where = $where->with($searchWordConstraint);
4669  }
4670  }
4671 
4672  if ((string)$where === '') {
4673  return '';
4674  }
4675 
4676  return ' AND (' . (string)$where . ')';
4677  }
4678 
4688  public function exec_getQuery($table, $conf)
4689  {
4690  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
4691  $statement = $this->getQuery($connection, $table, $conf);
4692 
4693  return $connection->executeQuery($statement);
4694  }
4695 
4705  public function getRecords($tableName, array $queryConfiguration)
4706  {
4707  $records = [];
4708 
4709  $statement = $this->exec_getQuery($tableName, $queryConfiguration);
4710 
4711  $pageRepository = $this->getPageRepository();
4712  while ($row = $statement->fetchAssociative()) {
4713  // Versioning preview:
4714  $pageRepository->versionOL($tableName, $row, true);
4715 
4716  // Language overlay:
4717  if (is_array($row)) {
4718  $row = $pageRepository->getLanguageOverlay($tableName, $row);
4719  }
4720 
4721  // Might be unset in the language overlay
4722  if (is_array($row)) {
4723  $records[] = $row;
4724  }
4725  }
4726 
4727  return $records;
4728  }
4729 
4743  public function getQuery(‪Connection $connection, string $table, array $conf): string
4744  {
4745  // Resolve stdWrap in these properties first
4746  $properties = [
4747  'pidInList',
4748  'uidInList',
4749  'languageField',
4750  'selectFields',
4751  'max',
4752  'begin',
4753  'groupBy',
4754  'orderBy',
4755  'join',
4756  'leftjoin',
4757  'rightjoin',
4758  'recursive',
4759  'where',
4760  ];
4761  foreach ($properties as $property) {
4762  $conf[$property] = trim(
4763  isset($conf[$property . '.'])
4764  ? (string)$this->stdWrap($conf[$property] ?? '', $conf[$property . '.'] ?? [])
4765  : (string)($conf[$property] ?? '')
4766  );
4767  if ($conf[$property] === '') {
4768  unset($conf[$property]);
4769  } elseif (in_array($property, ['languageField', 'selectFields', 'join', 'leftjoin', 'rightjoin', 'where'], true)) {
4770  $conf[$property] = QueryHelper::quoteDatabaseIdentifiers($connection, $conf[$property]);
4771  }
4772  if (isset($conf[$property . '.'])) {
4773  // stdWrapping already done, so remove the sub-array
4774  unset($conf[$property . '.']);
4775  }
4776  }
4777  // Handle PDO-style named parameter markers first
4778  $queryMarkers = $this->getQueryMarkers($connection, $conf);
4779  // Replace the markers in the non-stdWrap properties
4780  foreach ($queryMarkers as $marker => $markerValue) {
4781  $properties = [
4782  'uidInList',
4783  'selectFields',
4784  'where',
4785  'max',
4786  'begin',
4787  'groupBy',
4788  'orderBy',
4789  'join',
4790  'leftjoin',
4791  'rightjoin',
4792  ];
4793  foreach ($properties as $property) {
4794  if ($conf[$property] ?? false) {
4795  $conf[$property] = str_replace('###' . $marker . '###', $markerValue, $conf[$property]);
4796  }
4797  }
4798  }
4799 
4800  // Construct WHERE clause:
4801  // Handle recursive function for the pidInList
4802  if (isset($conf['recursive'])) {
4803  $conf['recursive'] = (int)$conf['recursive'];
4804  if ($conf['recursive'] > 0) {
4805  $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true);
4806  array_walk($pidList, function (&$storagePid) {
4807  if ($storagePid === 'this') {
4808  $storagePid = $this->getRequest()->getAttribute('frontend.page.information')->getId();
4809  }
4810  });
4811  $pageRepository = $this->getPageRepository();
4812  $expandedPidList = $pageRepository->getPageIdsRecursive($pidList, $conf['recursive']);
4813  $conf['pidInList'] = implode(',', $expandedPidList);
4814  }
4815  }
4816  if ((string)($conf['pidInList'] ?? '') === '') {
4817  $conf['pidInList'] = 'this';
4818  }
4819 
4820  $queryParts = $this->getQueryConstraints($connection, $table, $conf);
4821 
4822  $queryBuilder = $connection->‪createQueryBuilder();
4823  // @todo Check against getQueryConstraints, can probably use FrontendRestrictions
4824  // @todo here and remove enableFields there.
4825  $queryBuilder->getRestrictions()->removeAll();
4826  $queryBuilder->select('*')->from($table);
4827 
4828  if ($queryParts['where'] ?? false) {
4829  $queryBuilder->where($queryParts['where']);
4830  }
4831 
4832  if ($queryParts['groupBy'] ?? false) {
4833  $queryBuilder->groupBy(...$queryParts['groupBy']);
4834  }
4835 
4836  if (is_array($queryParts['orderBy'] ?? false)) {
4837  foreach ($queryParts['orderBy'] as $orderBy) {
4838  $queryBuilder->addOrderBy(...$orderBy);
4839  }
4840  }
4841 
4842  // Fields:
4843  if ($conf['selectFields'] ?? false) {
4844  $queryBuilder->selectLiteral($this->sanitizeSelectPart($connection, $conf['selectFields'], $table));
4845  }
4846 
4847  // Setting LIMIT:
4848  if (($conf['max'] ?? false) || ($conf['begin'] ?? false)) {
4849  // Finding the total number of records, if used:
4850  if (str_contains(strtolower(($conf['begin'] ?? '') . ($conf['max'] ?? '')), 'total')) {
4851  $countQueryBuilder = $connection->‪createQueryBuilder();
4852  $countQueryBuilder->getRestrictions()->removeAll();
4853  $countQueryBuilder->count('*')
4854  ->from($table)
4855  ->where($queryParts['where']);
4856 
4857  if ($queryParts['groupBy']) {
4858  $countQueryBuilder->groupBy(...$queryParts['groupBy']);
4859  }
4860 
4861  try {
4862  $count = $countQueryBuilder->executeQuery()->fetchOne();
4863  if (isset($conf['max'])) {
4864  $conf['max'] = str_ireplace('total', $count, (string)$conf['max']);
4865  }
4866  if (isset($conf['begin'])) {
4867  $conf['begin'] = str_ireplace('total', $count, (string)$conf['begin']);
4868  }
4869  } catch (DBALException $e) {
4870  $this->getTimeTracker()->setTSlogMessage($e->getPrevious()->getMessage());
4871  return '';
4872  }
4873  }
4874 
4875  if (isset($conf['begin']) && $conf['begin'] > 0) {
4876  $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['begin'])), 0);
4877  $queryBuilder->setFirstResult($conf['begin']);
4878  }
4879  if (isset($conf['max'])) {
4880  $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['max'])), 0);
4881  $queryBuilder->setMaxResults($conf['max'] ?: 100000);
4882  }
4883  }
4884 
4885  // Setting up tablejoins:
4886  if ($conf['join'] ?? false) {
4887  $joinParts = QueryHelper::parseJoin($conf['join']);
4888  $queryBuilder->join(
4889  $table,
4890  $joinParts['tableName'],
4891  $joinParts['tableAlias'],
4892  $joinParts['joinCondition']
4893  );
4894  } elseif ($conf['leftjoin'] ?? false) {
4895  $joinParts = QueryHelper::parseJoin($conf['leftjoin']);
4896  $queryBuilder->leftJoin(
4897  $table,
4898  $joinParts['tableName'],
4899  $joinParts['tableAlias'],
4900  $joinParts['joinCondition']
4901  );
4902  } elseif ($conf['rightjoin'] ?? false) {
4903  $joinParts = QueryHelper::parseJoin($conf['rightjoin']);
4904  $queryBuilder->rightJoin(
4905  $table,
4906  $joinParts['tableName'],
4907  $joinParts['tableAlias'],
4908  $joinParts['joinCondition']
4909  );
4910  }
4911 
4912  // Convert the QueryBuilder object into a SQL statement.
4913  $query = $queryBuilder->getSQL();
4914 
4915  // Replace the markers in the queryParts to handle stdWrap enabled properties
4916  foreach ($queryMarkers as $marker => $markerValue) {
4917  // @todo Ugly hack that needs to be cleaned up, with the current architecture
4918  // @todo for exec_Query / getQuery it's the best we can do.
4919  $query = str_replace('###' . $marker . '###', $markerValue, $query);
4920  }
4921  return $query;
4922  }
4923 
4933  protected function getQueryConstraints(Connection $connection, string $table, array $conf): array
4934  {
4935  $queryBuilder = $connection->createQueryBuilder();
4936  $expressionBuilder = $queryBuilder->expr();
4937  $request = $this->getRequest();
4938  $contentPid = $request->getAttribute('frontend.page.information')->getContentFromPid();
4939  $constraints = [];
4940  $pid_uid_flag = 0;
4941  $enableFieldsIgnore = [];
4942  $queryParts = [
4943  'where' => null,
4944  'groupBy' => null,
4945  'orderBy' => null,
4946  ];
4947 
4948  $isInWorkspace = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('workspace', 'isOffline');
4949  $considerMovePointers = (
4950  $isInWorkspace && $table !== 'pages'
4951  && !empty(‪$GLOBALS['TCA'][$table]['ctrl']['versioningWS'])
4952  );
4953 
4954  if (trim($conf['uidInList'] ?? '')) {
4955  $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['uidInList']));
4956 
4957  // If moved records shall be considered, select via t3ver_oid
4958  if ($considerMovePointers) {
4959  $constraints[] = (string)$expressionBuilder->or(
4960  $expressionBuilder->in($table . '.uid', $listArr),
4961  $expressionBuilder->and(
4962  $expressionBuilder->eq(
4963  $table . '.t3ver_state',
4964  VersionState::MOVE_POINTER->value
4965  ),
4966  $expressionBuilder->in($table . '.t3ver_oid', $listArr)
4967  )
4968  );
4969  } else {
4970  $constraints[] = (string)$expressionBuilder->in($table . '.uid', $listArr);
4971  }
4972  $pid_uid_flag++;
4973  }
4974 
4975  // Static_* tables are allowed to be fetched from root page
4976  if (str_starts_with($table, 'static_')) {
4977  $pid_uid_flag++;
4978  }
4979 
4980  if (trim($conf['pidInList'])) {
4981  $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['pidInList']));
4982  // Removes all pages which are not visible for the user!
4983  $listArr = $this->checkPidArray($listArr);
4984  if (GeneralUtility::inList($conf['pidInList'], 'root')) {
4985  $listArr[] = 0;
4986  }
4987  if (GeneralUtility::inList($conf['pidInList'], '-1')) {
4988  $listArr[] = -1;
4989  $enableFieldsIgnore['pid'] = true;
4990  }
4991  if (!empty($listArr)) {
4992  $constraints[] = $expressionBuilder->in($table . '.pid', array_map('intval', $listArr));
4993  $pid_uid_flag++;
4994  } else {
4995  // If not uid and not pid then uid is set to 0 - which results in nothing!!
4996  $pid_uid_flag = 0;
4997  }
4998  }
4999 
5000  // If not uid and not pid then uid is set to 0 - which results in nothing!!
5001  if (!$pid_uid_flag) {
5002  $constraints[] = $expressionBuilder->eq($table . '.uid', 0);
5003  }
5004 
5005  $where = trim((string)$this->stdWrapValue('where', $conf));
5006  if ($where) {
5007  $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where);
5008  }
5009 
5010  // Check if the default language should be fetched (= doing overlays), or if only the records of a language should be fetched
5011  // but only do this for TCA tables that have languages enabled
5012  $languageConstraint = $this->getLanguageRestriction($expressionBuilder, $table, $conf, GeneralUtility::makeInstance(Context::class));
5013  if ($languageConstraint !== null) {
5014  $constraints[] = $languageConstraint;
5015  }
5016 
5017  // default constraints from TCA
5018  $pageRepository = $this->getPageRepository();
5019  $constraints = array_merge(
5020  $constraints,
5021  array_values($pageRepository->getDefaultConstraints($table, $enableFieldsIgnore))
5022  );
5023 
5024  // MAKE WHERE:
5025  if ($constraints !== []) {
5026  $queryParts['where'] = $expressionBuilder->and(...$constraints);
5027  }
5028  // GROUP BY
5029  $groupBy = trim((string)$this->stdWrapValue('groupBy', $conf));
5030  if ($groupBy) {
5031  $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy);
5032  }
5033 
5034  // ORDER BY
5035  $orderByString = trim((string)$this->stdWrapValue('orderBy', $conf));
5036  if ($orderByString) {
5037  $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString);
5038  }
5039 
5040  // Return result:
5041  return $queryParts;
5042  }
5043 
5066  protected function getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context)
5067  {
5068  $languageField = '';
5069  $localizationParentField = ‪$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? null;
5070  // Check if the table is translatable, and set the language field by default from the TCA information
5071  if (!empty($conf['languageField']) || !isset($conf['languageField'])) {
5072  if (isset($conf['languageField']) && !empty(‪$GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) {
5073  $languageField = $conf['languageField'];
5074  } elseif (!empty(‪$GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($localizationParentField)) {
5075  $languageField = $table . '.' . ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'];
5076  }
5077  }
5078 
5079  // No language restriction enabled explicitly or available via TCA
5080  if (empty($languageField)) {
5081  return null;
5082  }
5083 
5085  $languageAspect = $context->getAspect('language');
5086  if ($languageAspect->doOverlays() && !empty($localizationParentField)) {
5087  // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will
5088  // OVERLAY the records with localized versions!
5089  $languageQuery = $expressionBuilder->in($languageField, [0, -1]);
5090  // Use this option to include records that don't have a default language counterpart ("free mode")
5091  // (originalpointerfield is 0 and the language field contains the requested language)
5092  if (isset($conf['includeRecordsWithoutDefaultTranslation']) || !empty($conf['includeRecordsWithoutDefaultTranslation.'])) {
5093  $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.'])
5094  ? $this->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.'])
5095  : $conf['includeRecordsWithoutDefaultTranslation'];
5096  $includeRecordsWithoutDefaultTranslation = trim($includeRecordsWithoutDefaultTranslation) !== '';
5097  } else {
5098  // Option was not explicitly set, check what's in for the language overlay type.
5099  $includeRecordsWithoutDefaultTranslation = $languageAspect->getOverlayType() === $languageAspect::OVERLAYS_ON_WITH_FLOATING;
5100  }
5101  if ($includeRecordsWithoutDefaultTranslation) {
5102  $languageQuery = $expressionBuilder->or(
5103  $languageQuery,
5104  $expressionBuilder->and(
5105  $expressionBuilder->eq($table . '.' . $localizationParentField, 0),
5106  $expressionBuilder->eq($languageField, $languageAspect->getContentId())
5107  )
5108  );
5109  }
5110  return $languageQuery;
5111  }
5112  // No overlays = only fetch records given for the requested language and "all languages"
5113  return $expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]);
5114  }
5115 
5126  protected function sanitizeSelectPart(Connection $connection, string $selectPart, string $table)
5127  {
5128  // Pattern matching parts
5129  $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)';
5130  $matchEnd = '(\\s*,|\\s*$)/';
5131  $necessaryFields = ['uid', 'pid'];
5132  $wsFields = ['t3ver_state'];
5133  $languageField = ‪$GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? false;
5134  if (isset(‪$GLOBALS['TCA'][$table]) && !preg_match($matchStart . '\\*' . $matchEnd, $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)|distinct/i', $selectPart)) {
5135  foreach ($necessaryFields as $field) {
5136  $match = $matchStart . $field . $matchEnd;
5137  if (!preg_match($match, $selectPart)) {
5138  $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field);
5139  }
5140  }
5141  if (is_string($languageField)) {
5142  $match = $matchStart . $languageField . $matchEnd;
5143  if (!preg_match($match, $selectPart)) {
5144  $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $languageField) . ' AS ' . $connection->quoteIdentifier($languageField);
5145  }
5146  }
5147  if (‪$GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ?? false) {
5148  foreach ($wsFields as $field) {
5149  $match = $matchStart . $field . $matchEnd;
5150  if (!preg_match($match, $selectPart)) {
5151  $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field);
5152  }
5153  }
5154  }
5155  }
5156  return $selectPart;
5157  }
5158 
5166  public function checkPidArray($pageIds)
5167  {
5168  if (!is_array($pageIds) || empty($pageIds)) {
5169  return [];
5170  }
5171 
5172  if ($pageIds === [$this->getRequest()->getAttribute('frontend.page.information')->getId()]) {
5173  // Middlewares already checked access to the current page and made sure the current doktype
5174  // is a doktype whose content should be rendered, so there is no need to check that again.
5175  return $pageIds;
5176  }
5177  $pageRepository = $this->getPageRepository();
5178  $restrictionContainer = GeneralUtility::makeInstance(FrontendRestrictionContainer::class);
5179  if ($this->checkPid_badDoktypeList) {
5180  $restrictionContainer->add(GeneralUtility::makeInstance(
5181  DocumentTypeExclusionRestriction::class,
5182  // @todo this functionality should be streamlined with a default FrontendRestriction or a "LinkRestrictionContainer"
5183  GeneralUtility::intExplode(',', (string)$this->checkPid_badDoktypeList, true)
5184  ));
5185  }
5186  return $pageRepository->filterAccessiblePageIds($pageIds, $restrictionContainer);
5187  }
5188 
5198  public function getQueryMarkers(Connection $connection, $conf)
5199  {
5200  if (!isset($conf['markers.']) || !is_array($conf['markers.'])) {
5201  return [];
5202  }
5203  $markerValues = [];
5204  foreach ($conf['markers.'] as $dottedMarker => $dummy) {
5205  $marker = rtrim($dottedMarker, '.');
5206  if ($dottedMarker != $marker . '.') {
5207  continue;
5208  }
5209  // Parse definition
5210  // todo else value is always null
5211  $tempValue = isset($conf['markers.'][$dottedMarker])
5212  ? $this->stdWrap($conf['markers.'][$dottedMarker]['value'] ?? '', $conf['markers.'][$dottedMarker])
5213  : $conf['markers.'][$dottedMarker]['value'];
5214  // Quote/escape if needed
5215  if (is_numeric($tempValue)) {
5216  if ((int)$tempValue == $tempValue) {
5217  // Handle integer
5218  $markerValues[$marker] = (int)$tempValue;
5219  } else {
5220  // Handle float
5221  $markerValues[$marker] = (float)$tempValue;
5222  }
5223  } elseif ($tempValue === null) {
5224  // It represents NULL
5225  $markerValues[$marker] = 'NULL';
5226  } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) {
5227  // See if it is really a comma separated list of values
5228  $explodeValues = GeneralUtility::trimExplode(',', $tempValue);
5229  if (count($explodeValues) > 1) {
5230  // Handle each element of list separately
5231  $tempArray = [];
5232  foreach ($explodeValues as $listValue) {
5233  if (is_numeric($listValue)) {
5234  if ((int)$listValue == $listValue) {
5235  $tempArray[] = (int)$listValue;
5236  } else {
5237  $tempArray[] = (float)$listValue;
5238  }
5239  } else {
5240  // If quoted, remove quotes before
5241  // escaping.
5242  if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) {
5243  $listValue = $matches[1];
5244  } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) {
5245  $listValue = $matches[1];
5246  }
5247  $tempArray[] = $connection->quote($listValue);
5248  }
5249  }
5250  $markerValues[$marker] = implode(',', $tempArray);
5251  } else {
5252  // Handle remaining values as string
5253  $markerValues[$marker] = $connection->quote($tempValue);
5254  }
5255  } else {
5256  // Handle remaining values as string
5257  $markerValues[$marker] = $connection->quote($tempValue);
5258  }
5259  }
5260  return $markerValues;
5261  }
5262 
5263  protected function getResourceFactory(): ResourceFactory
5264  {
5265  return GeneralUtility::makeInstance(ResourceFactory::class);
5266  }
5267 
5268  protected function getPageRepository(): ‪PageRepository
5269  {
5270  return GeneralUtility::makeInstance(PageRepository::class);
5271  }
5272 
5280  protected function getEnvironmentVariable($key)
5281  {
5282  if ($key === 'REQUEST_URI') {
5283  return $this->getRequest()->getAttribute('normalizedParams')->getRequestUri();
5284  }
5285  return GeneralUtility::getIndpEnv($key);
5286  }
5287 
5295  protected function getFromCache(array $configuration)
5296  {
5297  if (!$this->getRequest()->getAttribute('frontend.cache.instruction')->isCachingAllowed()) {
5298  return false;
5299  }
5300  $cacheKey = $this->calculateCacheKey($configuration);
5301  if (empty($cacheKey)) {
5302  return false;
5303  }
5304 
5305  $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('hash');
5306  $cachedData = $cacheFrontend->get($cacheKey);
5307  if ($cachedData === false) {
5308  return false;
5309  }
5310  $this->getTypoScriptFrontendController()->addCacheTags($cachedData['cacheTags'] ?? []);
5311  return $cachedData['content'] ?? false;
5312  }
5313 
5319  protected function calculateCacheLifetime(array $configuration)
5320  {
5321  $configuration['lifetime'] = $configuration['lifetime'] ?? '';
5322  $lifetimeConfiguration = (string)$this->stdWrapValue('lifetime', $configuration);
5323 
5324  $lifetime = null; // default lifetime
5325  if (strtolower($lifetimeConfiguration) === 'unlimited') {
5326  $lifetime = 0; // unlimited
5327  } elseif ($lifetimeConfiguration > 0) {
5328  $lifetime = (int)$lifetimeConfiguration; // lifetime in seconds
5329  }
5330  return $lifetime;
5331  }
5332 
5338  protected function calculateCacheTags(array $configuration)
5339  {
5340  $configuration['tags'] = $configuration['tags'] ?? '';
5341  $tags = (string)$this->stdWrapValue('tags', $configuration);
5342  return empty($tags) ? [] : GeneralUtility::trimExplode(',', $tags);
5343  }
5344 
5350  protected function calculateCacheKey(array $configuration)
5351  {
5352  $configuration['key'] = $configuration['key'] ?? '';
5353  return $this->stdWrapValue('key', $configuration);
5354  }
5355 
5359  protected function getTimeTracker()
5360  {
5361  return GeneralUtility::makeInstance(TimeTracker::class);
5362  }
5367  public function getTypoScriptFrontendController(): ?‪TypoScriptFrontendController
5368  {
5369  return $this->typoScriptFrontendController ?: ‪$GLOBALS['TSFE'] ?? null;
5370  }
5371 
5379  protected function getContentLengthOfCurrentTag(string $theValue, int $pointer, string $currentTag): int
5380  {
5381  $tempContent = strtolower(substr($theValue, $pointer));
5382  $startTag = '<' . $currentTag;
5383  $endTag = '</' . $currentTag . '>';
5384  $offsetCount = 0;
5385 
5386  // Take care for nested tags
5387  do {
5388  $nextMatchingEndTagPosition = strpos($tempContent, $endTag);
5389  // only match tag `a` in `<a href"...">` but not in `<abbr>`
5390  $nextSameTypeTagPosition = preg_match(
5391  '#' . $startTag . '[\s/>]#',
5392  $tempContent,
5393  $nextSameStartTagMatches,
5394  PREG_OFFSET_CAPTURE
5395  ) ? $nextSameStartTagMatches[0][1] : false;
5396 
5397  // filter out nested tag contents to help getting the correct closing tag
5398  if ($nextMatchingEndTagPosition !== false && $nextSameTypeTagPosition !== false && $nextSameTypeTagPosition < $nextMatchingEndTagPosition) {
5399  $lastOpeningTagStartPosition = (int)strrpos(substr($tempContent, 0, $nextMatchingEndTagPosition), $startTag);
5400  $closingTagEndPosition = $nextMatchingEndTagPosition + strlen($endTag);
5401  $offsetCount += $closingTagEndPosition - $lastOpeningTagStartPosition;
5402 
5403  // replace content from latest tag start to latest tag end
5404  $tempContent = substr($tempContent, 0, $lastOpeningTagStartPosition) . substr($tempContent, $closingTagEndPosition);
5405  }
5406  } while (
5407  ($nextMatchingEndTagPosition !== false && $nextSameTypeTagPosition !== false) &&
5408  $nextSameTypeTagPosition < $nextMatchingEndTagPosition
5409  );
5410 
5411  // if no closing tag is found we use length of the whole content
5412  $endingOffset = strlen($tempContent);
5413  if ($nextMatchingEndTagPosition !== false) {
5414  $endingOffset = $nextMatchingEndTagPosition + $offsetCount;
5415  }
5416 
5417  return $endingOffset;
5418  }
5419 
5420  protected function shallDebug(): bool
5421  {
5422  $typoScriptConfigArray = $this->getRequest()->getAttribute('frontend.typoscript')->getConfigArray();
5423  if (isset($typoScriptConfigArray['debug'])) {
5424  return (bool)($typoScriptConfigArray['debug']);
5425  }
5426  return !empty(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['debug']);
5427  }
5428 
5446  public function getRequest(): ServerRequestInterface
5447  {
5448  if ($this->request instanceof ServerRequestInterface) {
5449  return $this->request;
5450  }
5451  if ((‪$GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface) {
5452  // @todo: We may want to deprecate this fallback and force consumers
5453  // to setRequest() after object instantiation / unserialization instead.
5454  return ‪$GLOBALS['TYPO3_REQUEST'];
5455  }
5456  throw new ContentRenderingException(
5457  'PSR-7 request is missing in ContentObjectRenderer. Inject with start(), setRequest() or provide via $GLOBALS[\'TYPO3_REQUEST\'].',
5458  1607172972
5459  );
5460  }
5461 }
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_noTrimWrap
‪string stdWrap_noTrimWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2108
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$parentRecordNumber
‪int $parentRecordNumber
Definition: ContentObjectRenderer.php:306
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\http_makelinks
‪string http_makelinks(string $data, array $conf)
Definition: ContentObjectRenderer.php:3428
‪TYPO3\CMS\Core\Resource\FileReference\getProperty
‪getProperty(string $key)
Definition: FileReference.php:108
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\mergeTSRef
‪array mergeTSRef(array $typoScriptArray, string $propertyName)
Definition: ContentObjectRenderer.php:4575
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_case
‪string stdWrap_case($content='', $conf=[])
Definition: ContentObjectRenderer.php:1813
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_htmlSpecialChars
‪string stdWrap_htmlSpecialChars($content='', $conf=[])
Definition: ContentObjectRenderer.php:1903
‪TYPO3\CMS\Core\Database\Query\Restriction\DocumentTypeExclusionRestriction
Definition: DocumentTypeExclusionRestriction.php:27
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\shallDebug
‪shallDebug()
Definition: ContentObjectRenderer.php:5404
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setCurrentFile
‪setCurrentFile($fileObject)
Definition: ContentObjectRenderer.php:1010
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_expandList
‪string stdWrap_expandList($content='')
Definition: ContentObjectRenderer.php:1702
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getFileDataKey
‪string int getFileDataKey($key)
Definition: ContentObjectRenderer.php:4065
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\OBJECTTYPE_USER
‪const OBJECTTYPE_USER
Definition: ContentObjectRenderer.php:365
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_cacheRead
‪string stdWrap_cacheRead($content='', $conf=[])
Definition: ContentObjectRenderer.php:1162
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getCurrentTable
‪string getCurrentTable()
Definition: ContentObjectRenderer.php:455
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\determineExceptionHandlerClassName
‪determineExceptionHandlerClassName(array $configuration)
Definition: ContentObjectRenderer.php:678
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\listNum
‪string listNum($content, $listNum, $delimeter=',')
Definition: ContentObjectRenderer.php:2418
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\in
‪in(string $fieldName, $value)
Definition: ExpressionBuilder.php:227
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_ifNull
‪string stdWrap_ifNull($content='', $conf=[])
Definition: ContentObjectRenderer.php:1365
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder
Definition: ExpressionBuilder.php:40
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_insertData
‪string stdWrap_insertData($content='')
Definition: ContentObjectRenderer.php:2237
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\cObjGetSeparated
‪list< string > cObjGetSeparated(?array $setup, string $addKey='')
Definition: ContentObjectRenderer.php:528
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getUserObjectType
‪mixed getUserObjectType()
Definition: ContentObjectRenderer.php:723
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:37
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_orderedStdWrap
‪string stdWrap_orderedStdWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2208
‪TYPO3\CMS\Core\Resource\FileInterface
Definition: FileInterface.php:26
‪TYPO3\CMS\Core\Utility\DebugUtility\debugTrail
‪static string debugTrail(bool $prependFileNames=false)
Definition: DebugUtility.php:64
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_csConv
‪string stdWrap_csConv($content='', $conf=[])
Definition: ContentObjectRenderer.php:1538
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\round
‪string round($content, array $conf=[])
Definition: ContentObjectRenderer.php:2908
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\encaps_lineSplit
‪string encaps_lineSplit($theValue, $conf)
Definition: ContentObjectRenderer.php:3307
‪TYPO3\CMS\Core\Html\HtmlParser
Definition: HtmlParser.php:26
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\start
‪start($data, $table='')
Definition: ContentObjectRenderer.php:436
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\imageLinkWrap
‪string imageLinkWrap($string, $imageFile, $conf)
Definition: ContentObjectRenderer.php:845
‪debug
‪debug(mixed $variable='', ?string $title=null)
Definition: GlobalDebugFunctions.php:21
‪TYPO3\CMS\Core\Context\Context\getAspect
‪getAspect(string $name)
Definition: Context.php:76
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\substring
‪string substring($content, $options)
Definition: ContentObjectRenderer.php:2643
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_listNum
‪string stdWrap_listNum($content='', $conf=[])
Definition: ContentObjectRenderer.php:1414
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\or
‪or(CompositeExpression|\Doctrine\DBAL\Query\Expression\CompositeExpression|string|null ... $expressions)
Definition: ExpressionBuilder.php:59
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_prefixComment
‪string stdWrap_prefixComment($content='', $conf=[])
Definition: ContentObjectRenderer.php:2286
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setUserObjectType
‪setUserObjectType($userObjectType)
Definition: ContentObjectRenderer.php:733
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_wrap
‪string stdWrap_wrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2090
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getQuery
‪string getQuery(Connection $connection, string $table, array $conf)
Definition: ContentObjectRenderer.php:4727
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_replacement
‪string stdWrap_replacement($content='', $conf=[])
Definition: ContentObjectRenderer.php:1600
‪TYPO3\CMS\Core\Versioning\VersionState
‪VersionState
Definition: VersionState.php:22
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area
Definition: Area.php:23
‪TYPO3\CMS\Core\Resource\ProcessedFile\CONTEXT_IMAGECROPSCALEMASK
‪const CONTEXT_IMAGECROPSCALEMASK
Definition: ProcessedFile.php:61
‪TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException
Definition: MissingArrayPathException.php:27
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$currentRecord
‪string $currentRecord
Definition: ContentObjectRenderer.php:294
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$doConvertToUserIntObject
‪bool $doConvertToUserIntObject
Definition: ContentObjectRenderer.php:328
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterContentObjectRendererInitializedEvent
Definition: AfterContentObjectRendererInitializedEvent.php:26
‪TYPO3\CMS\Frontend\ContentObject
Definition: AbstractContentObject.php:18
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\cropHTML
‪string cropHTML(string $content, string $options)
Definition: ContentObjectRenderer.php:2689
‪TYPO3\CMS\Core\Utility\MathUtility\calculateWithParentheses
‪static string calculateWithParentheses(string $string)
Definition: MathUtility.php:167
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\parseFuncInternal
‪string parseFuncInternal($theValue, $conf)
Definition: ContentObjectRenderer.php:3094
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setParent
‪setParent($data, $currentRecord)
Definition: ContentObjectRenderer.php:468
‪TYPO3\CMS\Frontend\Resource\FilePathSanitizer
Definition: FilePathSanitizer.php:39
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_prepend
‪string stdWrap_prepend($content='', $conf=[])
Definition: ContentObjectRenderer.php:2164
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_stdWrap
‪string stdWrap_stdWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:1470
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\eq
‪eq(string $fieldName, $value)
Definition: ExpressionBuilder.php:84
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getTypoScriptFrontendController
‪getTypoScriptFrontendController()
Definition: ContentObjectRenderer.php:5351
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setCurrentVal
‪setCurrentVal($value)
Definition: ContentObjectRenderer.php:500
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterImageResourceResolvedEvent
Definition: AfterImageResourceResolvedEvent.php:28
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\parseFunc
‪string parseFunc($theValue, ?array $conf, ?string $ref=null)
Definition: ContentObjectRenderer.php:2962
‪TYPO3\CMS\Core\Page\PageLayoutResolver
Definition: PageLayoutResolver.php:42
‪TYPO3\CMS\Core\Resource\FileReference
Definition: FileReference.php:37
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getQueryConstraints
‪array getQueryConstraints(Connection $connection, string $table, array $conf)
Definition: ContentObjectRenderer.php:4917
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\HTMLcaseshift
‪string HTMLcaseshift($theValue, $case)
Definition: ContentObjectRenderer.php:4500
‪TYPO3\CMS\Core\Utility\DebugUtility\viewArray
‪static string viewArray(mixed $array_in)
Definition: DebugUtility.php:113
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getContentLengthOfCurrentTag
‪getContentLengthOfCurrentTag(string $theValue, int $pointer, string $currentTag)
Definition: ContentObjectRenderer.php:5363
‪TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface
Definition: ExceptionHandlerInterface.php:24
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:36
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_round
‪string stdWrap_round($content='', $conf=[])
Definition: ContentObjectRenderer.php:1677
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$currentRecordNumber
‪int $currentRecordNumber
Definition: ContentObjectRenderer.php:300
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getCurrentFile
‪File FileReference Folder FileInterface FolderInterface string null getCurrentFile()
Definition: ContentObjectRenderer.php:1020
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_stripHtml
‪string stdWrap_stripHtml($content='')
Definition: ContentObjectRenderer.php:1864
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_field
‪string null stdWrap_field($content='', $conf=[])
Definition: ContentObjectRenderer.php:1267
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_HTMLparser
‪string stdWrap_HTMLparser($content='', $conf=[])
Definition: ContentObjectRenderer.php:1570
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_lang
‪string stdWrap_lang($content='', $conf=[])
Definition: ContentObjectRenderer.php:1223
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getValueFromRecursiveData
‪getValueFromRecursiveData(array $keys, mixed $startValue)
Definition: ContentObjectRenderer.php:4173
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_br
‪string stdWrap_br($content='')
Definition: ContentObjectRenderer.php:1946
‪TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException
Definition: ContentRenderingException.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_wrapAlign
‪string stdWrap_wrapAlign($content='', $conf=[])
Definition: ContentObjectRenderer.php:2054
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getEnvironmentVariable
‪string getEnvironmentVariable($key)
Definition: ContentObjectRenderer.php:5264
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getCropAreaFromFileReference
‪Area null getCropAreaFromFileReference(FileReference $fileReference, array $fileArray)
Definition: ContentObjectRenderer.php:3660
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_split
‪string stdWrap_split($content='', $conf=[])
Definition: ContentObjectRenderer.php:1587
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getResourceFactory
‪getResourceFactory()
Definition: ContentObjectRenderer.php:5247
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_numRows
‪string stdWrap_numRows($content='', $conf=[])
Definition: ContentObjectRenderer.php:1309
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\cObjGetSingle
‪string cObjGetSingle(string $name, $conf, $TSkey='__')
Definition: ContentObjectRenderer.php:554
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_if
‪string stdWrap_if($content='', $conf=[])
Definition: ContentObjectRenderer.php:1501
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$currentFile
‪File FileReference Folder FolderInterface FileInterface string null $currentFile
Definition: ContentObjectRenderer.php:323
‪TYPO3\CMS\Core\Text\TextCropper
Definition: TextCropper.php:21
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\checkIf
‪checkIf($conf)
Definition: ContentObjectRenderer.php:2443
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrapValue
‪string int bool null stdWrapValue($key, array $config, $defaultValue='')
Definition: ContentObjectRenderer.php:1139
‪TYPO3\CMS\Core\Service\FlexFormService
Definition: FlexFormService.php:25
‪TYPO3\CMS\Core\Localization\DateFormatter
Definition: DateFormatter.php:27
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\wrap
‪string wrap($content, $wrap, $char='|')
Definition: ContentObjectRenderer.php:4359
‪TYPO3\CMS\Core\Utility\ArrayUtility\getValueByPath
‪static getValueByPath(array $array, array|string $path, string $delimiter='/')
Definition: ArrayUtility.php:176
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\exec_getQuery
‪Result exec_getQuery($table, $conf)
Definition: ContentObjectRenderer.php:4672
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getLanguageRestriction
‪string TYPO3 CMS Core Database Query Expression CompositeExpression null getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context)
Definition: ContentObjectRenderer.php:5050
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$userObjectType
‪int bool $userObjectType
Definition: ContentObjectRenderer.php:335
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_brTag
‪string stdWrap_brTag($content='', $conf=[])
Definition: ContentObjectRenderer.php:1960
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_rawUrlEncode
‪string stdWrap_rawUrlEncode($content='')
Definition: ContentObjectRenderer.php:1889
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Type\BitSet
Definition: BitSet.php:66
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_wrap3
‪string stdWrap_wrap3($content='', $conf=[])
Definition: ContentObjectRenderer.php:2191
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_formattedDate
‪string stdWrap_formattedDate(string $content, array $conf)
Definition: ContentObjectRenderer.php:1772
‪TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression
Definition: CompositeExpression.php:27
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:44
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_preIfEmptyListNum
‪string stdWrap_preIfEmptyListNum($content='', $conf=[])
Definition: ContentObjectRenderer.php:1352
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\caseshift
‪string caseshift($theValue, $case)
Definition: ContentObjectRenderer.php:4458
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$parentRecord
‪array $parentRecord
Definition: ContentObjectRenderer.php:312
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_date
‪string stdWrap_date($content='', $conf=[])
Definition: ContentObjectRenderer.php:1716
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\mergeExceptionHandlerConfiguration
‪mergeExceptionHandlerConfiguration(array $configuration)
Definition: ContentObjectRenderer.php:702
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_htmlSanitize
‪stdWrap_htmlSanitize(string $content='', array $conf=[])
Definition: ContentObjectRenderer.php:2298
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_keywords
‪string stdWrap_keywords($content='')
Definition: ContentObjectRenderer.php:1986
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_preCObject
‪string stdWrap_preCObject($content='', $conf=[])
Definition: ContentObjectRenderer.php:2027
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_required
‪string stdWrap_required($content='')
Definition: ContentObjectRenderer.php:1483
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_current
‪string stdWrap_current($content='', $conf=[])
Definition: ContentObjectRenderer.php:1281
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\__sleep
‪array __sleep()
Definition: ContentObjectRenderer.php:385
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_setContentToCurrent
‪string stdWrap_setContentToCurrent($content='')
Definition: ContentObjectRenderer.php:1195
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap
‪string null stdWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:1039
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\cObjGet
‪string cObjGet($setup, $addKey='')
Definition: ContentObjectRenderer.php:514
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_numberFormat
‪string stdWrap_numberFormat($content='', $conf=[])
Definition: ContentObjectRenderer.php:1690
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_setCurrent
‪string stdWrap_setCurrent($content='', $conf=[])
Definition: ContentObjectRenderer.php:1209
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\convertToUserIntObject
‪convertToUserIntObject()
Definition: ContentObjectRenderer.php:741
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_intval
‪string stdWrap_intval($content='')
Definition: ContentObjectRenderer.php:1646
‪TYPO3\CMS\Core\Utility\GeneralUtility\expandList
‪static string expandList($list)
Definition: GeneralUtility.php:434
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\insertData
‪string insertData($str)
Definition: ContentObjectRenderer.php:2581
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\__construct
‪__construct(TypoScriptFrontendController $typoScriptFrontendController=null, ContainerInterface $container=null)
Definition: ContentObjectRenderer.php:367
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_ifEmpty
‪string stdWrap_ifEmpty($content='', $conf=[])
Definition: ContentObjectRenderer.php:1379
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\createUrl
‪string createUrl(array $conf)
Definition: ContentObjectRenderer.php:4321
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_parseFunc
‪string stdWrap_parseFunc($content='', $conf=[])
Definition: ContentObjectRenderer.php:1556
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_hash
‪string stdWrap_hash($content='', array $conf=[])
Definition: ContentObjectRenderer.php:1659
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$data
‪array $data
Definition: ContentObjectRenderer.php:273
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:38
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Core\Database\Connection\createQueryBuilder
‪createQueryBuilder()
Definition: Connection.php:114
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\__wakeup
‪__wakeup()
Definition: ContentObjectRenderer.php:404
‪TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException
Definition: ResourceDoesNotExistException.php:23
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_outerWrap
‪string stdWrap_outerWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2225
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\numberFormat
‪string numberFormat($content, $conf)
Definition: ContentObjectRenderer.php:2936
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\keywords
‪string keywords($content)
Definition: ContentObjectRenderer.php:4438
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\typoLink
‪string LinkResult typoLink(string $linkText, array $conf)
Definition: ContentObjectRenderer.php:4252
‪TYPO3\CMS\Core\Html\SanitizerInitiator
Definition: SanitizerInitiator.php:28
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\noTrimWrap
‪string noTrimWrap($content, $wrap, $char='|')
Definition: ContentObjectRenderer.php:4378
‪TYPO3\CMS\Core\Database\Connection\quoteIdentifier
‪string quoteIdentifier(string $identifier)
Definition: Connection.php:129
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getFieldVal
‪string null getFieldVal($field)
Definition: ContentObjectRenderer.php:3733
‪TYPO3\CMS\Core\Page\DefaultJavaScriptAssetTrait
Definition: DefaultJavaScriptAssetTrait.php:30
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\HTMLparser_TSbridge
‪string HTMLparser_TSbridge($theValue, $conf)
Definition: ContentObjectRenderer.php:2545
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$currentValKey
‪string $currentValKey
Definition: ContentObjectRenderer.php:287
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\createCropAreaFromJsonString
‪createCropAreaFromJsonString(string $cropSettings, string $cropVariant)
Definition: ContentObjectRenderer.php:3717
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_postCObject
‪string stdWrap_postCObject($content='', $conf=[])
Definition: ContentObjectRenderer.php:2040
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\dataWrap
‪string dataWrap($content, $wrap)
Definition: ContentObjectRenderer.php:2561
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getRecords
‪array getRecords($tableName, array $queryConfiguration)
Definition: ContentObjectRenderer.php:4689
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_strftime
‪string stdWrap_strftime($content='', $conf=[])
Definition: ContentObjectRenderer.php:1733
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\calculateCacheTags
‪array calculateCacheTags(array $configuration)
Definition: ContentObjectRenderer.php:5322
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject
Definition: AbstractContentObject.php:31
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_substring
‪string stdWrap_substring($content='', $conf=[])
Definition: ContentObjectRenderer.php:1839
‪TYPO3\CMS\Core\Context\LanguageAspect
Definition: LanguageAspect.php:57
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\mailto_makelinks
‪string mailto_makelinks(string $data, array $conf)
Definition: ContentObjectRenderer.php:3489
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_innerWrap2
‪string stdWrap_innerWrap2($content='', $conf=[])
Definition: ContentObjectRenderer.php:2014
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_ifBlank
‪string stdWrap_ifBlank($content='', $conf=[])
Definition: ContentObjectRenderer.php:1396
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getContentObject
‪getContentObject($name)
Definition: ContentObjectRenderer.php:584
‪TYPO3\CMS\Core\Utility\DebugUtility
Definition: DebugUtility.php:27
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$lastTypoLinkResult
‪LinkResultInterface $lastTypoLinkResult
Definition: ContentObjectRenderer.php:319
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\createExceptionHandler
‪createExceptionHandler(array $configuration)
Definition: ContentObjectRenderer.php:661
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$parameters
‪array $parameters
Definition: ContentObjectRenderer.php:283
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_debug
‪string stdWrap_debug($content='')
Definition: ContentObjectRenderer.php:2361
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getRequest
‪getRequest()
Definition: ContentObjectRenderer.php:5430
‪TYPO3\CMS\Frontend\Imaging\GifBuilder
Definition: GifBuilder.php:58
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getPageRepository
‪getPageRepository()
Definition: ContentObjectRenderer.php:5252
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$request
‪ServerRequestInterface $request
Definition: ContentObjectRenderer.php:352
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_typolink
‪string stdWrap_typolink($content='', $conf=[])
Definition: ContentObjectRenderer.php:2073
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_dataWrap
‪string stdWrap_dataWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2151
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$stdWrapRecursionLevel
‪int $stdWrapRecursionLevel
Definition: ContentObjectRenderer.php:343
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$stopRendering
‪array $stopRendering
Definition: ContentObjectRenderer.php:339
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\createLink
‪createLink(string $linkText, array $conf)
Definition: ContentObjectRenderer.php:4298
‪TYPO3\CMS\Core\Resource\ProcessedFile
Definition: ProcessedFile.php:47
‪TYPO3\CMS\Core\TypoScript\TypoScriptService
Definition: TypoScriptService.php:27
‪$output
‪$output
Definition: annotationChecker.php:114
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\calculateCacheLifetime
‪int null calculateCacheLifetime(array $configuration)
Definition: ContentObjectRenderer.php:5303
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$container
‪ContainerInterface null $container
Definition: ContentObjectRenderer.php:108
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_encapsLines
‪string stdWrap_encapsLines($content='', $conf=[])
Definition: ContentObjectRenderer.php:1974
‪TYPO3\CMS\Core\Resource\Exception
Definition: Exception.php:21
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\calc
‪int calc($val)
Definition: ContentObjectRenderer.php:2711
‪TYPO3\CMS\Webhooks\Message\$url
‪identifier readonly UriInterface $url
Definition: LoginErrorOccurredMessage.php:36
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_append
‪string stdWrap_append($content='', $conf=[])
Definition: ContentObjectRenderer.php:2177
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\render
‪render(AbstractContentObject $contentObject, $configuration=[])
Definition: ContentObjectRenderer.php:607
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_bytes
‪string stdWrap_bytes($content='', $conf=[])
Definition: ContentObjectRenderer.php:1826
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_crop
‪string stdWrap_crop($content='', $conf=[])
Definition: ContentObjectRenderer.php:1877
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getData
‪string getData($string, $fieldArray=null)
Definition: ContentObjectRenderer.php:3759
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$stdWrapOrder
‪array $stdWrapOrder
Definition: ContentObjectRenderer.php:116
‪TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
Definition: TypoScriptFrontendController.php:58
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Frontend\ContentObject\Event\BeforeStdWrapFunctionsInitializedEvent
Definition: BeforeStdWrapFunctionsInitializedEvent.php:23
‪TYPO3\CMS\Core\TimeTracker\TimeTracker\setTSlogMessage
‪setTSlogMessage(string $content, string $logLevel=LogLevel::INFO)
Definition: TimeTracker.php:143
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:26
‪TYPO3\CMS\Frontend\ContentObject\Event\BeforeStdWrapFunctionsExecutedEvent
Definition: BeforeStdWrapFunctionsExecutedEvent.php:23
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_debugData
‪string stdWrap_debugData($content='')
Definition: ContentObjectRenderer.php:2387
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder\and
‪and(CompositeExpression|\Doctrine\DBAL\Query\Expression\CompositeExpression|string|null ... $expressions,)
Definition: ExpressionBuilder.php:50
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_wrap2
‪string stdWrap_wrap2($content='', $conf=[])
Definition: ContentObjectRenderer.php:2133
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getCropAreaFromFromTypoScriptSettings
‪Area null getCropAreaFromFromTypoScriptSettings(FileInterface $file, array $fileArray)
Definition: ContentObjectRenderer.php:3679
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\crop
‪string crop($content, $options)
Definition: ContentObjectRenderer.php:2661
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_cObject
‪string stdWrap_cObject($content='', $conf=[])
Definition: ContentObjectRenderer.php:1295
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_prioriCalc
‪string stdWrap_prioriCalc($content='', $conf=[])
Definition: ContentObjectRenderer.php:1614
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\sanitizeSelectPart
‪string sanitizeSelectPart(Connection $connection, string $selectPart, string $table)
Definition: ContentObjectRenderer.php:5110
‪TYPO3\CMS\Core\Utility\GeneralUtility\revExplode
‪static list< string > revExplode(string $delimiter, string $string, int $limit=0)
Definition: GeneralUtility.php:787
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\replacementSingle
‪string replacementSingle($content, array $configuration)
Definition: ContentObjectRenderer.php:2839
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\lastChanged
‪lastChanged($tstamp)
Definition: ContentObjectRenderer.php:990
‪TYPO3\CMS\Core\Resource\FolderInterface
Definition: FolderInterface.php:24
‪TYPO3\CMS\Core\Html\HtmlCropper
Definition: HtmlCropper.php:24
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_char
‪string stdWrap_char($content='', $conf=[])
Definition: ContentObjectRenderer.php:1634
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
Definition: ContentObjectRenderer.php:102
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\numRows
‪int numRows($conf)
Definition: ContentObjectRenderer.php:2402
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$typoScriptFrontendController
‪TypoScriptFrontendController null $typoScriptFrontendController
Definition: ContentObjectRenderer.php:347
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Core\Utility\StringUtility\multibyteStringPad
‪static multibyteStringPad(string $string, int $length, string $pad_string=' ', int $pad_type=STR_PAD_RIGHT, string $encoding='UTF-8')
Definition: StringUtility.php:131
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\setRequest
‪setRequest(ServerRequestInterface $request)
Definition: ContentObjectRenderer.php:373
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_debugFunc
‪string stdWrap_debugFunc($content='', $conf=[])
Definition: ContentObjectRenderer.php:2374
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getTimeTracker
‪TimeTracker getTimeTracker()
Definition: ContentObjectRenderer.php:5343
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\rootLineValue
‪string rootLineValue($key, $field, $slideBack=false, $altRootLine='')
Definition: ContentObjectRenderer.php:4130
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\calcAge
‪string calcAge($seconds, $labels=null)
Definition: ContentObjectRenderer.php:4528
‪TYPO3\CMS\Core\Domain\Repository\PageRepository
Definition: PageRepository.php:69
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\prefixComment
‪string prefixComment($str, $conf, $content)
Definition: ContentObjectRenderer.php:2618
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\searchWhere
‪string searchWhere($searchWords, $searchFieldList, $searchTable)
Definition: ContentObjectRenderer.php:4624
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterStdWrapFunctionsInitializedEvent
Definition: AfterStdWrapFunctionsInitializedEvent.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\callUserFunction
‪mixed callUserFunction($funcName, $conf, $content)
Definition: ContentObjectRenderer.php:4398
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_data
‪string stdWrap_data($_='', $conf=[])
Definition: ContentObjectRenderer.php:1254
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_override
‪string stdWrap_override($content='', $conf=[])
Definition: ContentObjectRenderer.php:1335
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getQueryMarkers
‪array getQueryMarkers(Connection $connection, $conf)
Definition: ContentObjectRenderer.php:5182
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\readFlexformIntoConf
‪readFlexformIntoConf($flexData, &$conf, $recursive=false)
Definition: ContentObjectRenderer.php:762
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_innerWrap
‪string stdWrap_innerWrap($content='', $conf=[])
Definition: ContentObjectRenderer.php:2000
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getSlidePids
‪string getSlidePids($pidList, $pidConf)
Definition: ContentObjectRenderer.php:808
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_age
‪string stdWrap_age($content='', $conf=[])
Definition: ContentObjectRenderer.php:1799
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$table
‪string $table
Definition: ContentObjectRenderer.php:277
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterStdWrapFunctionsExecutedEvent
Definition: AfterStdWrapFunctionsExecutedEvent.php:23
‪TYPO3\CMS\Frontend\ContentObject\Event\AfterGetDataResolvedEvent
Definition: AfterGetDataResolvedEvent.php:26
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getImgResource
‪ImageResource null getImgResource($file, $fileArray)
Definition: ContentObjectRenderer.php:3542
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_cropHTML
‪string stdWrap_cropHTML($content='', $conf=[])
Definition: ContentObjectRenderer.php:1852
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getCurrentVal
‪mixed getCurrentVal()
Definition: ContentObjectRenderer.php:489
‪TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer
Definition: FrontendRestrictionContainer.php:30
‪TYPO3\CMS\Core\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Core\Utility\GeneralUtility\intExplode
‪static list< int > intExplode(string $delimiter, string $string, bool $removeEmptyValues=false)
Definition: GeneralUtility.php:756
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:34
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\checkPidArray
‪array checkPidArray($pageIds)
Definition: ContentObjectRenderer.php:5150
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getFromCache
‪string bool getFromCache(array $configuration)
Definition: ContentObjectRenderer.php:5279
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_addPageCacheTags
‪string stdWrap_addPageCacheTags($content='', $conf=[])
Definition: ContentObjectRenderer.php:1178
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_strtotime
‪string stdWrap_strtotime($content='', $conf=[])
Definition: ContentObjectRenderer.php:1755
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\OBJECTTYPE_USER_INT
‪const OBJECTTYPE_USER_INT
Definition: ContentObjectRenderer.php:359
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_trim
‪string stdWrap_trim($content='')
Definition: ContentObjectRenderer.php:1426
‪TYPO3\CMS\Core\Core\Environment\getContext
‪static getContext()
Definition: Environment.php:128
‪TYPO3\CMS\Core\Resource\Exception
Definition: AbstractFileOperationException.php:16
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_preUserFunc
‪string stdWrap_preUserFunc($content='', $conf=[])
Definition: ContentObjectRenderer.php:1322
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\calculateCacheKey
‪string calculateCacheKey(array $configuration)
Definition: ContentObjectRenderer.php:5334
‪TYPO3\CMS\Core\Utility\GeneralUtility\xml2array
‪static array string xml2array(string $string, string $NSprefix='', bool $reportDocTag=false)
Definition: GeneralUtility.php:1265
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getKey
‪int getKey($key, $arr)
Definition: ContentObjectRenderer.php:4214
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_postUserFuncInt
‪string stdWrap_postUserFuncInt($content='', $conf=[])
Definition: ContentObjectRenderer.php:2264
‪TYPO3\CMS\Core\Html\SanitizerBuilderFactory
Definition: SanitizerBuilderFactory.php:37
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_doubleBrTag
‪string stdWrap_doubleBrTag($content='', $conf=[])
Definition: ContentObjectRenderer.php:1933
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\typoLink_URL
‪string typoLink_URL($conf)
Definition: ContentObjectRenderer.php:4338
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\splitObj
‪string splitObj($value, $conf)
Definition: ContentObjectRenderer.php:2753
‪TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler
Definition: ProductionExceptionHandler.php:33
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_strPad
‪string stdWrap_strPad($content='', $conf=[])
Definition: ContentObjectRenderer.php:1439
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Frontend\ContentObject\AbstractContentObject\render
‪string render($conf=[])
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_encodeForJavaScriptValue
‪string stdWrap_encodeForJavaScriptValue($content='')
Definition: ContentObjectRenderer.php:1920
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\$checkPid_badDoktypeList
‪string int null $checkPid_badDoktypeList
Definition: ContentObjectRenderer.php:317
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\replacement
‪string replacement($content, array $configuration)
Definition: ContentObjectRenderer.php:2819
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_fieldRequired
‪string stdWrap_fieldRequired($content='', $conf=[])
Definition: ContentObjectRenderer.php:1519
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_postUserFunc
‪string stdWrap_postUserFunc($content='', $conf=[])
Definition: ContentObjectRenderer.php:2250
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\getGlobal
‪mixed getGlobal($keyString, $source=null)
Definition: ContentObjectRenderer.php:4158
‪TYPO3\CMS\Frontend\ContentObject\Event\BeforeStdWrapContentStoredInCacheEvent
Definition: BeforeStdWrapContentStoredInCacheEvent.php:30
‪TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer\stdWrap_cacheStore
‪string null stdWrap_cacheStore($content='', $conf=[])
Definition: ContentObjectRenderer.php:2321