[BUGFIX] Accept Unicode characters in email spam protection
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / ContentObjectRenderer.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Doctrine\DBAL\DBALException;
18 use Doctrine\DBAL\Driver\Statement;
19 use Psr\Log\LoggerAwareInterface;
20 use Psr\Log\LoggerAwareTrait;
21 use TYPO3\CMS\Core\Cache\CacheManager;
22 use TYPO3\CMS\Core\Context\Context;
23 use TYPO3\CMS\Core\Context\LanguageAspect;
24 use TYPO3\CMS\Core\Core\Environment;
25 use TYPO3\CMS\Core\Database\Connection;
26 use TYPO3\CMS\Core\Database\ConnectionPool;
27 use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
28 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
29 use TYPO3\CMS\Core\Database\Query\QueryHelper;
30 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
31 use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
32 use TYPO3\CMS\Core\Html\HtmlParser;
33 use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
34 use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
35 use TYPO3\CMS\Core\LinkHandling\LinkService;
36 use TYPO3\CMS\Core\Log\LogManager;
37 use TYPO3\CMS\Core\Mail\MailMessage;
38 use TYPO3\CMS\Core\Resource\Exception;
39 use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
40 use TYPO3\CMS\Core\Resource\File;
41 use TYPO3\CMS\Core\Resource\FileInterface;
42 use TYPO3\CMS\Core\Resource\FileReference;
43 use TYPO3\CMS\Core\Resource\Folder;
44 use TYPO3\CMS\Core\Resource\ProcessedFile;
45 use TYPO3\CMS\Core\Resource\ResourceFactory;
46 use TYPO3\CMS\Core\Resource\StorageRepository;
47 use TYPO3\CMS\Core\Service\DependencyOrderingService;
48 use TYPO3\CMS\Core\Service\FlexFormService;
49 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
50 use TYPO3\CMS\Core\Site\Entity\Site;
51 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
52 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
53 use TYPO3\CMS\Core\TypoScript\TypoScriptService;
54 use TYPO3\CMS\Core\Utility\ArrayUtility;
55 use TYPO3\CMS\Core\Utility\DebugUtility;
56 use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
57 use TYPO3\CMS\Core\Utility\GeneralUtility;
58 use TYPO3\CMS\Core\Utility\HttpUtility;
59 use TYPO3\CMS\Core\Utility\MailUtility;
60 use TYPO3\CMS\Core\Utility\MathUtility;
61 use TYPO3\CMS\Core\Utility\PathUtility;
62 use TYPO3\CMS\Core\Utility\StringUtility;
63 use TYPO3\CMS\Core\Versioning\VersionState;
64 use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
65 use TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface;
66 use TYPO3\CMS\Frontend\ContentObject\Exception\ProductionExceptionHandler;
67 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
68 use TYPO3\CMS\Frontend\Http\UrlProcessorInterface;
69 use TYPO3\CMS\Frontend\Imaging\GifBuilder;
70 use TYPO3\CMS\Frontend\Page\PageRepository;
71 use TYPO3\CMS\Frontend\Resource\FilePathSanitizer;
72 use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
73 use TYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder;
74 use TYPO3\CMS\Frontend\Typolink\UnableToLinkException;
75
76 /**
77 * This class contains all main TypoScript features.
78 * This includes the rendering of TypoScript content objects (cObjects).
79 * Is the backbone of TypoScript Template rendering.
80 *
81 * There are lots of functions you can use from your include-scripts.
82 * The class is normally instantiated and referred to as "cObj".
83 * When you call your own PHP-code typically through a USER or USER_INT cObject then it is this class that instantiates the object and calls the main method. Before it does so it will set (if you are using classes) a reference to itself in the internal variable "cObj" of the object. Thus you can access all functions and data from this class by $this->cObj->... from within you classes written to be USER or USER_INT content objects.
84 */
85 class ContentObjectRenderer implements LoggerAwareInterface
86 {
87 use LoggerAwareTrait;
88
89 /**
90 * @var array
91 */
92 public $align = [
93 'center',
94 'right',
95 'left'
96 ];
97
98 /**
99 * stdWrap functions in their correct order
100 *
101 * @see stdWrap()
102 */
103 public $stdWrapOrder = [
104 'stdWrapPreProcess' => 'hook',
105 // this is a placeholder for the first Hook
106 'cacheRead' => 'hook',
107 // this is a placeholder for checking if the content is available in cache
108 'setContentToCurrent' => 'boolean',
109 'setContentToCurrent.' => 'array',
110 'addPageCacheTags' => 'string',
111 'addPageCacheTags.' => 'array',
112 'setCurrent' => 'string',
113 'setCurrent.' => 'array',
114 'lang.' => 'array',
115 'data' => 'getText',
116 'data.' => 'array',
117 'field' => 'fieldName',
118 'field.' => 'array',
119 'current' => 'boolean',
120 'current.' => 'array',
121 'cObject' => 'cObject',
122 'cObject.' => 'array',
123 'numRows.' => 'array',
124 // @deprecated - will be removed in TYPO3 v10.0.
125 'filelist' => 'dir',
126 // @deprecated - will be removed in TYPO3 v10.0.
127 'filelist.' => 'array',
128 'preUserFunc' => 'functionName',
129 'stdWrapOverride' => 'hook',
130 // this is a placeholder for the second Hook
131 'override' => 'string',
132 'override.' => 'array',
133 'preIfEmptyListNum' => 'listNum',
134 'preIfEmptyListNum.' => 'array',
135 'ifNull' => 'string',
136 'ifNull.' => 'array',
137 'ifEmpty' => 'string',
138 'ifEmpty.' => 'array',
139 'ifBlank' => 'string',
140 'ifBlank.' => 'array',
141 'listNum' => 'listNum',
142 'listNum.' => 'array',
143 'trim' => 'boolean',
144 'trim.' => 'array',
145 'strPad.' => 'array',
146 'stdWrap' => 'stdWrap',
147 'stdWrap.' => 'array',
148 'stdWrapProcess' => 'hook',
149 // this is a placeholder for the third Hook
150 'required' => 'boolean',
151 'required.' => 'array',
152 'if.' => 'array',
153 'fieldRequired' => 'fieldName',
154 'fieldRequired.' => 'array',
155 'csConv' => 'string',
156 'csConv.' => 'array',
157 'parseFunc' => 'objectpath',
158 'parseFunc.' => 'array',
159 'HTMLparser' => 'boolean',
160 'HTMLparser.' => 'array',
161 'split.' => 'array',
162 'replacement.' => 'array',
163 'prioriCalc' => 'boolean',
164 'prioriCalc.' => 'array',
165 'char' => 'integer',
166 'char.' => 'array',
167 'intval' => 'boolean',
168 'intval.' => 'array',
169 'hash' => 'string',
170 'hash.' => 'array',
171 'round' => 'boolean',
172 'round.' => 'array',
173 'numberFormat.' => 'array',
174 'expandList' => 'boolean',
175 'expandList.' => 'array',
176 'date' => 'dateconf',
177 'date.' => 'array',
178 'strtotime' => 'strtotimeconf',
179 'strtotime.' => 'array',
180 'strftime' => 'strftimeconf',
181 'strftime.' => 'array',
182 'age' => 'boolean',
183 'age.' => 'array',
184 'case' => 'case',
185 'case.' => 'array',
186 'bytes' => 'boolean',
187 'bytes.' => 'array',
188 'substring' => 'parameters',
189 'substring.' => 'array',
190 'cropHTML' => 'crop',
191 'cropHTML.' => 'array',
192 'stripHtml' => 'boolean',
193 'stripHtml.' => 'array',
194 'crop' => 'crop',
195 'crop.' => 'array',
196 'rawUrlEncode' => 'boolean',
197 'rawUrlEncode.' => 'array',
198 'htmlSpecialChars' => 'boolean',
199 'htmlSpecialChars.' => 'array',
200 'encodeForJavaScriptValue' => 'boolean',
201 'encodeForJavaScriptValue.' => 'array',
202 'doubleBrTag' => 'string',
203 'doubleBrTag.' => 'array',
204 'br' => 'boolean',
205 'br.' => 'array',
206 'brTag' => 'string',
207 'brTag.' => 'array',
208 'encapsLines.' => 'array',
209 'keywords' => 'boolean',
210 'keywords.' => 'array',
211 'innerWrap' => 'wrap',
212 'innerWrap.' => 'array',
213 'innerWrap2' => 'wrap',
214 'innerWrap2.' => 'array',
215 // @deprecated - will be removed in TYPO3 v10.0.
216 'addParams.' => 'array',
217 // @deprecated - will be removed in TYPO3 v10.0.
218 'filelink.' => 'array',
219 'preCObject' => 'cObject',
220 'preCObject.' => 'array',
221 'postCObject' => 'cObject',
222 'postCObject.' => 'array',
223 'wrapAlign' => 'align',
224 'wrapAlign.' => 'array',
225 'typolink.' => 'array',
226 'wrap' => 'wrap',
227 'wrap.' => 'array',
228 'noTrimWrap' => 'wrap',
229 'noTrimWrap.' => 'array',
230 'wrap2' => 'wrap',
231 'wrap2.' => 'array',
232 'dataWrap' => 'dataWrap',
233 'dataWrap.' => 'array',
234 'prepend' => 'cObject',
235 'prepend.' => 'array',
236 'append' => 'cObject',
237 'append.' => 'array',
238 'wrap3' => 'wrap',
239 'wrap3.' => 'array',
240 'orderedStdWrap' => 'stdWrap',
241 'orderedStdWrap.' => 'array',
242 'outerWrap' => 'wrap',
243 'outerWrap.' => 'array',
244 'insertData' => 'boolean',
245 'insertData.' => 'array',
246 'postUserFunc' => 'functionName',
247 'postUserFuncInt' => 'functionName',
248 'prefixComment' => 'string',
249 'prefixComment.' => 'array',
250 'editIcons' => 'string',
251 'editIcons.' => 'array',
252 'editPanel' => 'boolean',
253 'editPanel.' => 'array',
254 'cacheStore' => 'hook',
255 // this is a placeholder for storing the content in cache
256 'stdWrapPostProcess' => 'hook',
257 // this is a placeholder for the last Hook
258 'debug' => 'boolean',
259 'debug.' => 'array',
260 'debugFunc' => 'boolean',
261 'debugFunc.' => 'array',
262 'debugData' => 'boolean',
263 'debugData.' => 'array'
264 ];
265
266 /**
267 * Class names for accordant content object names
268 *
269 * @var array
270 */
271 protected $contentObjectClassMap = [];
272
273 /**
274 * Loaded with the current data-record.
275 *
276 * If the instance of this class is used to render records from the database those records are found in this array.
277 * The function stdWrap has TypoScript properties that fetch field-data from this array.
278 *
279 * @var array
280 * @see start()
281 */
282 public $data = [];
283
284 /**
285 * @var string
286 */
287 protected $table = '';
288
289 /**
290 * Used for backup
291 *
292 * @var array
293 */
294 public $oldData = [];
295
296 /**
297 * If this is set with an array before stdWrap, it's used instead of $this->data in the data-property in stdWrap
298 *
299 * @var string
300 */
301 public $alternativeData = '';
302
303 /**
304 * Used by the parseFunc function and is loaded with tag-parameters when parsing tags.
305 *
306 * @var array
307 */
308 public $parameters = [];
309
310 /**
311 * @var string
312 */
313 public $currentValKey = 'currentValue_kidjls9dksoje';
314
315 /**
316 * This is set to the [table]:[uid] of the record delivered in the $data-array, if the cObjects CONTENT or RECORD is in operation.
317 * Note that $GLOBALS['TSFE']->currentRecord is set to an equal value but always indicating the latest record rendered.
318 *
319 * @var string
320 */
321 public $currentRecord = '';
322
323 /**
324 * Set in RecordsContentObject and ContentContentObject to the current number of records selected in a query.
325 *
326 * @var int
327 */
328 public $currentRecordTotal = 0;
329
330 /**
331 * Incremented in RecordsContentObject and ContentContentObject before each record rendering.
332 *
333 * @var int
334 */
335 public $currentRecordNumber = 0;
336
337 /**
338 * Incremented in RecordsContentObject and ContentContentObject before each record rendering.
339 *
340 * @var int
341 */
342 public $parentRecordNumber = 0;
343
344 /**
345 * If the ContentObjectRender was started from ContentContentObject, RecordsContentObject or SearchResultContentObject this array has two keys, 'data' and 'currentRecord' which indicates the record and data for the parent cObj.
346 *
347 * @var array
348 */
349 public $parentRecord = [];
350
351 /**
352 * This is used by checkPid, that checks if pages are accessible. The $checkPid_cache['page_uid'] is set TRUE or FALSE upon this check featuring a caching function for the next request.
353 *
354 * @var array
355 */
356 public $checkPid_cache = [];
357
358 /**
359 * @var string
360 */
361 public $checkPid_badDoktypeList = '255';
362
363 /**
364 * This will be set by typoLink() to the url of the most recent link created.
365 *
366 * @var string
367 */
368 public $lastTypoLinkUrl = '';
369
370 /**
371 * DO. link target.
372 *
373 * @var string
374 */
375 public $lastTypoLinkTarget = '';
376
377 /**
378 * @var array
379 */
380 public $lastTypoLinkLD = [];
381
382 /**
383 * array that registers rendered content elements (or any table) to make sure they are not rendered recursively!
384 *
385 * @var array
386 */
387 public $recordRegister = [];
388
389 /**
390 * Additionally registered content object types and class names
391 *
392 * @var array
393 */
394 protected $cObjHookObjectsRegistry = [];
395
396 /**
397 * @var array
398 */
399 public $cObjHookObjectsArr = [];
400
401 /**
402 * Containing hook objects for stdWrap
403 *
404 * @var array
405 */
406 protected $stdWrapHookObjects = [];
407
408 /**
409 * Containing hook objects for getImgResource
410 *
411 * @var array
412 */
413 protected $getImgResourceHookObjects;
414
415 /**
416 * @var File Current file objects (during iterations over files)
417 */
418 protected $currentFile;
419
420 /**
421 * Set to TRUE by doConvertToUserIntObject() if USER object wants to become USER_INT
422 */
423 public $doConvertToUserIntObject = false;
424
425 /**
426 * Indicates current object type. Can hold one of OBJECTTYPE_ constants or FALSE.
427 * The value is set and reset inside USER() function. Any time outside of
428 * USER() it is FALSE.
429 */
430 protected $userObjectType = false;
431
432 /**
433 * @var array
434 */
435 protected $stopRendering = [];
436
437 /**
438 * @var int
439 */
440 protected $stdWrapRecursionLevel = 0;
441
442 /**
443 * @var TypoScriptFrontendController
444 */
445 protected $typoScriptFrontendController;
446
447 /**
448 * Indicates that object type is USER.
449 *
450 * @see ContentObjectRender::$userObjectType
451 */
452 const OBJECTTYPE_USER_INT = 1;
453 /**
454 * Indicates that object type is USER.
455 *
456 * @see ContentObjectRender::$userObjectType
457 */
458 const OBJECTTYPE_USER = 2;
459
460 /**
461 * @param TypoScriptFrontendController $typoScriptFrontendController
462 */
463 public function __construct(TypoScriptFrontendController $typoScriptFrontendController = null)
464 {
465 $this->typoScriptFrontendController = $typoScriptFrontendController;
466 $this->contentObjectClassMap = $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'];
467 }
468
469 /**
470 * Prevent several objects from being serialized.
471 * If currentFile is set, it is either a File or a FileReference object. As the object itself can't be serialized,
472 * we have store a hash and restore the object in __wakeup()
473 *
474 * @return array
475 */
476 public function __sleep()
477 {
478 $vars = get_object_vars($this);
479 unset($vars['typoScriptFrontendController'], $vars['logger']);
480 if ($this->currentFile instanceof FileReference) {
481 $this->currentFile = 'FileReference:' . $this->currentFile->getUid();
482 } elseif ($this->currentFile instanceof File) {
483 $this->currentFile = 'File:' . $this->currentFile->getIdentifier();
484 } else {
485 unset($vars['currentFile']);
486 }
487 return array_keys($vars);
488 }
489
490 /**
491 * Restore currentFile from hash.
492 * If currentFile references a File, the identifier equals file identifier.
493 * If it references a FileReference the identifier equals the uid of the reference.
494 */
495 public function __wakeup()
496 {
497 if (isset($GLOBALS['TSFE'])) {
498 $this->typoScriptFrontendController = $GLOBALS['TSFE'];
499 }
500 if ($this->currentFile !== null && is_string($this->currentFile)) {
501 list($objectType, $identifier) = explode(':', $this->currentFile, 2);
502 try {
503 if ($objectType === 'File') {
504 $this->currentFile = ResourceFactory::getInstance()->retrieveFileOrFolderObject($identifier);
505 } elseif ($objectType === 'FileReference') {
506 $this->currentFile = ResourceFactory::getInstance()->getFileReferenceObject($identifier);
507 }
508 } catch (ResourceDoesNotExistException $e) {
509 $this->currentFile = null;
510 }
511 }
512 $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
513 }
514
515 /**
516 * Allow injecting content object class map.
517 *
518 * This method is private API, please use configuration
519 * $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'] to add new content objects
520 *
521 * @internal
522 * @param array $contentObjectClassMap
523 */
524 public function setContentObjectClassMap(array $contentObjectClassMap)
525 {
526 $this->contentObjectClassMap = $contentObjectClassMap;
527 }
528
529 /**
530 * Register a single content object name to class name
531 *
532 * This method is private API, please use configuration
533 * $GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects'] to add new content objects
534 *
535 * @param string $className
536 * @param string $contentObjectName
537 * @internal
538 */
539 public function registerContentObjectClass($className, $contentObjectName)
540 {
541 $this->contentObjectClassMap[$contentObjectName] = $className;
542 }
543
544 /**
545 * Class constructor.
546 * Well, it has to be called manually since it is not a real constructor function.
547 * So after making an instance of the class, call this function and pass to it a database record and the tablename from where the record is from. That will then become the "current" record loaded into memory and accessed by the .fields property found in eg. stdWrap.
548 *
549 * @param array $data The record data that is rendered.
550 * @param string $table The table that the data record is from.
551 */
552 public function start($data, $table = '')
553 {
554 $this->data = $data;
555 $this->table = $table;
556 $this->currentRecord = $table !== ''
557 ? $table . ':' . ($this->data['uid'] ?? '')
558 : '';
559 $this->parameters = [];
560 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClass'] ?? [] as $classArr) {
561 $this->cObjHookObjectsRegistry[$classArr[0]] = $classArr[1];
562 }
563 $this->stdWrapHookObjects = [];
564 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap'] ?? [] as $className) {
565 $hookObject = GeneralUtility::makeInstance($className);
566 if (!$hookObject instanceof ContentObjectStdWrapHookInterface) {
567 throw new \UnexpectedValueException($className . ' must implement interface ' . ContentObjectStdWrapHookInterface::class, 1195043965);
568 }
569 $this->stdWrapHookObjects[] = $hookObject;
570 }
571 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['postInit'] ?? [] as $className) {
572 $postInitializationProcessor = GeneralUtility::makeInstance($className);
573 if (!$postInitializationProcessor instanceof ContentObjectPostInitHookInterface) {
574 throw new \UnexpectedValueException($className . ' must implement interface ' . ContentObjectPostInitHookInterface::class, 1274563549);
575 }
576 $postInitializationProcessor->postProcessContentObjectInitialization($this);
577 }
578 }
579
580 /**
581 * Returns the current table
582 *
583 * @return string
584 */
585 public function getCurrentTable()
586 {
587 return $this->table;
588 }
589
590 /**
591 * Gets the 'getImgResource' hook objects.
592 * The first call initializes the accordant objects.
593 *
594 * @return array The 'getImgResource' hook objects (if any)
595 */
596 protected function getGetImgResourceHookObjects()
597 {
598 if (!isset($this->getImgResourceHookObjects)) {
599 $this->getImgResourceHookObjects = [];
600 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImgResource'] ?? [] as $className) {
601 $hookObject = GeneralUtility::makeInstance($className);
602 if (!$hookObject instanceof ContentObjectGetImageResourceHookInterface) {
603 throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetImageResourceHookInterface::class, 1218636383);
604 }
605 $this->getImgResourceHookObjects[] = $hookObject;
606 }
607 }
608 return $this->getImgResourceHookObjects;
609 }
610
611 /**
612 * Sets the internal variable parentRecord with information about current record.
613 * If the ContentObjectRender was started from CONTENT, RECORD or SEARCHRESULT cObject's this array has two keys, 'data' and 'currentRecord' which indicates the record and data for the parent cObj.
614 *
615 * @param array $data The record array
616 * @param string $currentRecord This is set to the [table]:[uid] of the record delivered in the $data-array, if the cObjects CONTENT or RECORD is in operation. Note that $GLOBALS['TSFE']->currentRecord is set to an equal value but always indicating the latest record rendered.
617 * @internal
618 */
619 public function setParent($data, $currentRecord)
620 {
621 $this->parentRecord = [
622 'data' => $data,
623 'currentRecord' => $currentRecord
624 ];
625 }
626
627 /***********************************************
628 *
629 * CONTENT_OBJ:
630 *
631 ***********************************************/
632 /**
633 * Returns the "current" value.
634 * The "current" value is just an internal variable that can be used by functions to pass a single value on to another function later in the TypoScript processing.
635 * It's like "load accumulator" in the good old C64 days... basically a "register" you can use as you like.
636 * The TSref will tell if functions are setting this value before calling some other object so that you know if it holds any special information.
637 *
638 * @return mixed The "current" value
639 */
640 public function getCurrentVal()
641 {
642 return $this->data[$this->currentValKey];
643 }
644
645 /**
646 * Sets the "current" value.
647 *
648 * @param mixed $value The variable that you want to set as "current
649 * @see getCurrentVal()
650 */
651 public function setCurrentVal($value)
652 {
653 $this->data[$this->currentValKey] = $value;
654 }
655
656 /**
657 * Rendering of a "numerical array" of cObjects from TypoScript
658 * Will call ->cObjGetSingle() for each cObject found and accumulate the output.
659 *
660 * @param array $setup array with cObjects as values.
661 * @param string $addKey A prefix for the debugging information
662 * @return string Rendered output from the cObjects in the array.
663 * @see cObjGetSingle()
664 */
665 public function cObjGet($setup, $addKey = '')
666 {
667 if (!is_array($setup)) {
668 return '';
669 }
670 $sKeyArray = ArrayUtility::filterAndSortByNumericKeys($setup);
671 $content = '';
672 foreach ($sKeyArray as $theKey) {
673 $theValue = $setup[$theKey];
674 if ((int)$theKey && strpos($theKey, '.') === false) {
675 $conf = $setup[$theKey . '.'];
676 $content .= $this->cObjGetSingle($theValue, $conf, $addKey . $theKey);
677 }
678 }
679 return $content;
680 }
681
682 /**
683 * Renders a content object
684 *
685 * @param string $name The content object name, eg. "TEXT" or "USER" or "IMAGE
686 * @param array $conf The array with TypoScript properties for the content object
687 * @param string $TSkey A string label used for the internal debugging tracking.
688 * @return string cObject output
689 * @throws \UnexpectedValueException
690 */
691 public function cObjGetSingle($name, $conf, $TSkey = '__')
692 {
693 $content = '';
694 // Checking that the function is not called eternally. This is done by interrupting at a depth of 100
695 $this->getTypoScriptFrontendController()->cObjectDepthCounter--;
696 if ($this->getTypoScriptFrontendController()->cObjectDepthCounter > 0) {
697 $timeTracker = $this->getTimeTracker();
698 $name = trim($name);
699 if ($timeTracker->LR) {
700 $timeTracker->push($TSkey, $name);
701 }
702 // Checking if the COBJ is a reference to another object. (eg. name of 'blabla.blabla = < styles.something')
703 if (isset($name[0]) && $name[0] === '<') {
704 $key = trim(substr($name, 1));
705 $cF = GeneralUtility::makeInstance(TypoScriptParser::class);
706 // $name and $conf is loaded with the referenced values.
707 $confOverride = is_array($conf) ? $conf : [];
708 list($name, $conf) = $cF->getVal($key, $this->getTypoScriptFrontendController()->tmpl->setup);
709 $conf = array_replace_recursive(is_array($conf) ? $conf : [], $confOverride);
710 // Getting the cObject
711 $timeTracker->incStackPointer();
712 $content .= $this->cObjGetSingle($name, $conf, $key);
713 $timeTracker->decStackPointer();
714 } else {
715 $hooked = false;
716 // Application defined cObjects
717 if (!empty($this->cObjHookObjectsRegistry[$name])) {
718 if (empty($this->cObjHookObjectsArr[$name])) {
719 $this->cObjHookObjectsArr[$name] = GeneralUtility::makeInstance($this->cObjHookObjectsRegistry[$name]);
720 }
721 $hookObj = $this->cObjHookObjectsArr[$name];
722 if (method_exists($hookObj, 'cObjGetSingleExt')) {
723 $content .= $hookObj->cObjGetSingleExt($name, $conf, $TSkey, $this);
724 $hooked = true;
725 }
726 }
727 if (!$hooked) {
728 $contentObject = $this->getContentObject($name);
729 if ($contentObject) {
730 $content .= $this->render($contentObject, $conf);
731 } else {
732 // Call hook functions for extra processing
733 if ($name) {
734 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] ?? [] as $className) {
735 $hookObject = GeneralUtility::makeInstance($className);
736 if (!$hookObject instanceof ContentObjectGetSingleHookInterface) {
737 throw new \UnexpectedValueException('$hookObject must implement interface ' . ContentObjectGetSingleHookInterface::class, 1195043731);
738 }
739 /** @var ContentObjectGetSingleHookInterface $hookObject */
740 $content .= $hookObject->getSingleContentObject($name, (array)$conf, $TSkey, $this);
741 }
742 } else {
743 // Log error in AdminPanel
744 $warning = sprintf('Content Object "%s" does not exist', $name);
745 $timeTracker->setTSlogMessage($warning, 2);
746 }
747 }
748 }
749 }
750 if ($timeTracker->LR) {
751 $timeTracker->pull($content);
752 }
753 }
754 // Increasing on exit...
755 $this->getTypoScriptFrontendController()->cObjectDepthCounter++;
756 return $content;
757 }
758
759 /**
760 * Returns a new content object of type $name.
761 * This content object needs to be registered as content object
762 * in $this->contentObjectClassMap
763 *
764 * @param string $name
765 * @return AbstractContentObject|null
766 * @throws ContentRenderingException
767 */
768 public function getContentObject($name)
769 {
770 if (!isset($this->contentObjectClassMap[$name])) {
771 return null;
772 }
773 $fullyQualifiedClassName = $this->contentObjectClassMap[$name];
774 $contentObject = GeneralUtility::makeInstance($fullyQualifiedClassName, $this);
775 if (!($contentObject instanceof AbstractContentObject)) {
776 throw new ContentRenderingException(sprintf('Registered content object class name "%s" must be an instance of AbstractContentObject, but is not!', $fullyQualifiedClassName), 1422564295);
777 }
778 return $contentObject;
779 }
780
781 /********************************************
782 *
783 * Functions rendering content objects (cObjects)
784 *
785 ********************************************/
786
787 /**
788 * Renders a content object by taking exception and cache handling
789 * into consideration
790 *
791 * @param AbstractContentObject $contentObject Content object instance
792 * @param array $configuration Array of TypoScript properties
793 *
794 * @throws ContentRenderingException
795 * @throws \Exception
796 * @return string
797 */
798 public function render(AbstractContentObject $contentObject, $configuration = [])
799 {
800 $content = '';
801
802 // Evaluate possible cache and return
803 $cacheConfiguration = $configuration['cache.'] ?? null;
804 if ($cacheConfiguration !== null) {
805 unset($configuration['cache.']);
806 $cache = $this->getFromCache($cacheConfiguration);
807 if ($cache !== false) {
808 return $cache;
809 }
810 }
811
812 // Render content
813 try {
814 $content .= $contentObject->render($configuration);
815 } catch (ContentRenderingException $exception) {
816 // Content rendering Exceptions indicate a critical problem which should not be
817 // caught e.g. when something went wrong with Exception handling itself
818 throw $exception;
819 } catch (\Exception $exception) {
820 $exceptionHandler = $this->createExceptionHandler($configuration);
821 if ($exceptionHandler === null) {
822 throw $exception;
823 }
824 $content = $exceptionHandler->handle($exception, $contentObject, $configuration);
825 }
826
827 // Store cache
828 if ($cacheConfiguration !== null) {
829 $key = $this->calculateCacheKey($cacheConfiguration);
830 if (!empty($key)) {
831 /** @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheFrontend */
832 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
833 $tags = $this->calculateCacheTags($cacheConfiguration);
834 $lifetime = $this->calculateCacheLifetime($cacheConfiguration);
835 $cacheFrontend->set($key, $content, $tags, $lifetime);
836 }
837 }
838
839 return $content;
840 }
841
842 /**
843 * Creates the content object exception handler from local content object configuration
844 * or, from global configuration if not explicitly disabled in local configuration
845 *
846 * @param array $configuration
847 * @return ExceptionHandlerInterface|null
848 * @throws ContentRenderingException
849 */
850 protected function createExceptionHandler($configuration = [])
851 {
852 $exceptionHandler = null;
853 $exceptionHandlerClassName = $this->determineExceptionHandlerClassName($configuration);
854 if (!empty($exceptionHandlerClassName)) {
855 $exceptionHandler = GeneralUtility::makeInstance($exceptionHandlerClassName, $this->mergeExceptionHandlerConfiguration($configuration));
856 if (!$exceptionHandler instanceof ExceptionHandlerInterface) {
857 throw new ContentRenderingException('An exception handler was configured but the class does not exist or does not implement the ExceptionHandlerInterface', 1403653369);
858 }
859 }
860
861 return $exceptionHandler;
862 }
863
864 /**
865 * Determine exception handler class name from global and content object configuration
866 *
867 * @param array $configuration
868 * @return string|null
869 */
870 protected function determineExceptionHandlerClassName($configuration)
871 {
872 $exceptionHandlerClassName = null;
873 $tsfe = $this->getTypoScriptFrontendController();
874 if (!isset($tsfe->config['config']['contentObjectExceptionHandler'])) {
875 if (GeneralUtility::getApplicationContext()->isProduction()) {
876 $exceptionHandlerClassName = '1';
877 }
878 } else {
879 $exceptionHandlerClassName = $tsfe->config['config']['contentObjectExceptionHandler'];
880 }
881
882 if (isset($configuration['exceptionHandler'])) {
883 $exceptionHandlerClassName = $configuration['exceptionHandler'];
884 }
885
886 if ($exceptionHandlerClassName === '1') {
887 $exceptionHandlerClassName = ProductionExceptionHandler::class;
888 }
889
890 return $exceptionHandlerClassName;
891 }
892
893 /**
894 * Merges global exception handler configuration with the one from the content object
895 * and returns the merged exception handler configuration
896 *
897 * @param array $configuration
898 * @return array
899 */
900 protected function mergeExceptionHandlerConfiguration($configuration)
901 {
902 $exceptionHandlerConfiguration = [];
903 $tsfe = $this->getTypoScriptFrontendController();
904 if (!empty($tsfe->config['config']['contentObjectExceptionHandler.'])) {
905 $exceptionHandlerConfiguration = $tsfe->config['config']['contentObjectExceptionHandler.'];
906 }
907 if (!empty($configuration['exceptionHandler.'])) {
908 $exceptionHandlerConfiguration = array_replace_recursive($exceptionHandlerConfiguration, $configuration['exceptionHandler.']);
909 }
910
911 return $exceptionHandlerConfiguration;
912 }
913
914 /**
915 * Retrieves a type of object called as USER or USER_INT. Object can detect their
916 * type by using this call. It returns OBJECTTYPE_USER_INT or OBJECTTYPE_USER depending on the
917 * current object execution. In all other cases it will return FALSE to indicate
918 * a call out of context.
919 *
920 * @return mixed One of OBJECTTYPE_ class constants or FALSE
921 */
922 public function getUserObjectType()
923 {
924 return $this->userObjectType;
925 }
926
927 /**
928 * Sets the user object type
929 *
930 * @param mixed $userObjectType
931 */
932 public function setUserObjectType($userObjectType)
933 {
934 $this->userObjectType = $userObjectType;
935 }
936
937 /**
938 * Requests the current USER object to be converted to USER_INT.
939 */
940 public function convertToUserIntObject()
941 {
942 if ($this->userObjectType !== self::OBJECTTYPE_USER) {
943 $this->getTimeTracker()->setTSlogMessage(self::class . '::convertToUserIntObject() is called in the wrong context or for the wrong object type', 2);
944 } else {
945 $this->doConvertToUserIntObject = true;
946 }
947 }
948
949 /************************************
950 *
951 * Various helper functions for content objects:
952 *
953 ************************************/
954 /**
955 * Converts a given config in Flexform to a conf-array
956 *
957 * @param string|array $flexData Flexform data
958 * @param array $conf Array to write the data into, by reference
959 * @param bool $recursive Is set if called recursive. Don't call function with this parameter, it's used inside the function only
960 */
961 public function readFlexformIntoConf($flexData, &$conf, $recursive = false)
962 {
963 if ($recursive === false && is_string($flexData)) {
964 $flexData = GeneralUtility::xml2array($flexData, 'T3');
965 }
966 if (is_array($flexData) && isset($flexData['data']['sDEF']['lDEF'])) {
967 $flexData = $flexData['data']['sDEF']['lDEF'];
968 }
969 if (!is_array($flexData)) {
970 return;
971 }
972 foreach ($flexData as $key => $value) {
973 if (!is_array($value)) {
974 continue;
975 }
976 if (isset($value['el'])) {
977 if (is_array($value['el']) && !empty($value['el'])) {
978 foreach ($value['el'] as $ekey => $element) {
979 if (isset($element['vDEF'])) {
980 $conf[$ekey] = $element['vDEF'];
981 } else {
982 if (is_array($element)) {
983 $this->readFlexformIntoConf($element, $conf[$key][key($element)][$ekey], true);
984 } else {
985 $this->readFlexformIntoConf($element, $conf[$key][$ekey], true);
986 }
987 }
988 }
989 } else {
990 $this->readFlexformIntoConf($value['el'], $conf[$key], true);
991 }
992 }
993 if (isset($value['vDEF'])) {
994 $conf[$key] = $value['vDEF'];
995 }
996 }
997 }
998
999 /**
1000 * Returns all parents of the given PID (Page UID) list
1001 *
1002 * @param string $pidList A list of page Content-Element PIDs (Page UIDs) / stdWrap
1003 * @param array $pidConf stdWrap array for the list
1004 * @return string A list of PIDs
1005 * @internal
1006 */
1007 public function getSlidePids($pidList, $pidConf)
1008 {
1009 $pidList = isset($pidConf) ? trim($this->stdWrap($pidList, $pidConf)) : trim($pidList);
1010 if ($pidList === '') {
1011 $pidList = 'this';
1012 }
1013 $tsfe = $this->getTypoScriptFrontendController();
1014 $listArr = null;
1015 if (trim($pidList)) {
1016 $listArr = GeneralUtility::intExplode(',', str_replace('this', $tsfe->contentPid, $pidList));
1017 $listArr = $this->checkPidArray($listArr);
1018 }
1019 $pidList = [];
1020 if (is_array($listArr) && !empty($listArr)) {
1021 foreach ($listArr as $uid) {
1022 $page = $tsfe->sys_page->getPage($uid);
1023 if (!$page['is_siteroot']) {
1024 $pidList[] = $page['pid'];
1025 }
1026 }
1027 }
1028 return implode(',', $pidList);
1029 }
1030
1031 /**
1032 * Returns a <img> tag with the image file defined by $file and processed according to the properties in the TypoScript array.
1033 * Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript.
1034 * This function is called by "$this->cImage($conf['file'], $conf);" from IMAGE().
1035 *
1036 * @param string $file File TypoScript resource
1037 * @param array $conf TypoScript configuration properties
1038 * @return string <img> tag, (possibly wrapped in links and other HTML) if any image found.
1039 * @internal
1040 * @see IMAGE()
1041 */
1042 public function cImage($file, $conf)
1043 {
1044 $tsfe = $this->getTypoScriptFrontendController();
1045 $info = $this->getImgResource($file, $conf['file.']);
1046 $tsfe->lastImageInfo = $info;
1047 if (!is_array($info)) {
1048 return '';
1049 }
1050 if (is_file(Environment::getPublicPath() . '/' . $info['3'])) {
1051 $source = $tsfe->absRefPrefix . str_replace('%2F', '/', rawurlencode($info['3']));
1052 } else {
1053 $source = $info[3];
1054 }
1055
1056 $layoutKey = $this->stdWrap($conf['layoutKey'], $conf['layoutKey.']);
1057 $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
1058 $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
1059
1060 // This array is used to collect the image-refs on the page...
1061 $tsfe->imagesOnPage[] = $source;
1062 $altParam = $this->getAltParam($conf);
1063 $params = $this->stdWrapValue('params', $conf);
1064 if ($params !== '' && $params[0] !== ' ') {
1065 $params = ' ' . $params;
1066 }
1067
1068 $imageTagValues = [
1069 'width' => (int)$info[0],
1070 'height' => (int)$info[1],
1071 'src' => htmlspecialchars($source),
1072 'params' => $params,
1073 'altParams' => $altParam,
1074 'border' => $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
1075 'sourceCollection' => $sourceCollection,
1076 'selfClosingTagSlash' => !empty($tsfe->xhtmlDoctype) ? ' /' : '',
1077 ];
1078
1079 $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1080 $theValue = $markerTemplateEngine->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
1081
1082 $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
1083 if ($linkWrap) {
1084 $theValue = $this->linkWrap($theValue, $linkWrap);
1085 } elseif ($conf['imageLinkWrap']) {
1086 $originalFile = !empty($info['originalFile']) ? $info['originalFile'] : $info['origFile'];
1087 $theValue = $this->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
1088 }
1089 $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
1090 if ((string)$wrap !== '') {
1091 $theValue = $this->wrap($theValue, $conf['wrap']);
1092 }
1093 return $theValue;
1094 }
1095
1096 /**
1097 * Returns the 'border' attribute for an <img> tag only if the doctype is not xhtml_strict, xhtml_11 or html5
1098 * or if the config parameter 'disableImgBorderAttr' is not set.
1099 *
1100 * @param string $borderAttr The border attribute
1101 * @return string The border attribute
1102 */
1103 public function getBorderAttr($borderAttr)
1104 {
1105 $tsfe = $this->getTypoScriptFrontendController();
1106 $docType = $tsfe->xhtmlDoctype;
1107 if (
1108 $docType !== 'xhtml_strict' && $docType !== 'xhtml_11'
1109 && $tsfe->config['config']['doctype'] !== 'html5'
1110 && !$tsfe->config['config']['disableImgBorderAttr']
1111 ) {
1112 return $borderAttr;
1113 }
1114 return '';
1115 }
1116
1117 /**
1118 * Returns the html-template for rendering the image-Tag if no template is defined via typoscript the
1119 * default <img> tag template is returned
1120 *
1121 * @param string $layoutKey rendering key
1122 * @param array $conf TypoScript configuration properties
1123 * @return string
1124 */
1125 public function getImageTagTemplate($layoutKey, $conf)
1126 {
1127 if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
1128 $imageTagLayout = $this->stdWrap(
1129 $conf['layout.'][$layoutKey . '.']['element'] ?? '',
1130 $conf['layout.'][$layoutKey . '.']['element.'] ?? []
1131 );
1132 } else {
1133 $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1134 }
1135 return $imageTagLayout;
1136 }
1137
1138 /**
1139 * Render alternate sources for the image tag. If no source collection is given an empty string is returned.
1140 *
1141 * @param string $layoutKey rendering key
1142 * @param array $conf TypoScript configuration properties
1143 * @param string $file
1144 * @throws \UnexpectedValueException
1145 * @return string
1146 */
1147 public function getImageSourceCollection($layoutKey, $conf, $file)
1148 {
1149 $sourceCollection = '';
1150 if ($layoutKey
1151 && isset($conf['sourceCollection.']) && $conf['sourceCollection.']
1152 && (
1153 isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
1154 || isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
1155 )
1156 ) {
1157
1158 // find active sourceCollection
1159 $activeSourceCollections = [];
1160 foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
1161 if (substr($sourceCollectionKey, -1) === '.') {
1162 if (empty($sourceCollectionConfiguration['if.']) || $this->checkIf($sourceCollectionConfiguration['if.'])) {
1163 $activeSourceCollections[] = $sourceCollectionConfiguration;
1164 }
1165 }
1166 }
1167
1168 // apply option split to configurations
1169 $tsfe = $this->getTypoScriptFrontendController();
1170 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
1171 $srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
1172
1173 // render sources
1174 foreach ($activeSourceCollections as $key => $sourceConfiguration) {
1175 $sourceLayout = $this->stdWrap(
1176 $srcLayoutOptionSplitted[$key]['source'] ?? '',
1177 $srcLayoutOptionSplitted[$key]['source.'] ?? []
1178 );
1179
1180 $sourceRenderConfiguration = [
1181 'file' => $file,
1182 'file.' => $conf['file.'] ?? null
1183 ];
1184
1185 if (isset($sourceConfiguration['quality']) || isset($sourceConfiguration['quality.'])) {
1186 $imageQuality = $sourceConfiguration['quality'] ?? '';
1187 if (isset($sourceConfiguration['quality.'])) {
1188 $imageQuality = $this->stdWrap($sourceConfiguration['quality'], $sourceConfiguration['quality.']);
1189 }
1190 if ($imageQuality) {
1191 $sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
1192 }
1193 }
1194
1195 if (isset($sourceConfiguration['pixelDensity'])) {
1196 $pixelDensity = (int)$this->stdWrap(
1197 $sourceConfiguration['pixelDensity'] ?? '',
1198 $sourceConfiguration['pixelDensity.'] ?? []
1199 );
1200 } else {
1201 $pixelDensity = 1;
1202 }
1203 $dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
1204 foreach ($dimensionKeys as $dimensionKey) {
1205 $dimension = $this->stdWrap(
1206 $sourceConfiguration[$dimensionKey] ?? '',
1207 $sourceConfiguration[$dimensionKey . '.'] ?? []
1208 );
1209 if (!$dimension) {
1210 $dimension = $this->stdWrap(
1211 $conf['file.'][$dimensionKey] ?? '',
1212 $conf['file.'][$dimensionKey . '.'] ?? []
1213 );
1214 }
1215 if ($dimension) {
1216 if (strstr($dimension, 'c') !== false && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
1217 $dimensionParts = explode('c', $dimension, 2);
1218 $dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
1219 if ($dimensionParts[1]) {
1220 $dimension .= $dimensionParts[1];
1221 }
1222 } elseif ($dimensionKey === 'XY') {
1223 $dimensionParts = GeneralUtility::intExplode(',', $dimension, false, 2);
1224 $dimension = $dimensionParts[0] * $pixelDensity;
1225 if ($dimensionParts[1]) {
1226 $dimension .= ',' . $dimensionParts[1] * $pixelDensity;
1227 }
1228 } else {
1229 $dimension = (int)$dimension * $pixelDensity;
1230 }
1231 $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
1232 // Remove the stdWrap properties for dimension as they have been processed already above.
1233 unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
1234 }
1235 }
1236 $sourceInfo = $this->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
1237 if ($sourceInfo) {
1238 $sourceConfiguration['width'] = $sourceInfo[0];
1239 $sourceConfiguration['height'] = $sourceInfo[1];
1240 $urlPrefix = '';
1241 if (parse_url($sourceInfo[3], PHP_URL_HOST) === null) {
1242 $urlPrefix = $tsfe->absRefPrefix;
1243 }
1244 $sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $sourceInfo[3]);
1245 $sourceConfiguration['selfClosingTagSlash'] = !empty($tsfe->xhtmlDoctype) ? ' /' : '';
1246
1247 $markerTemplateEngine = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1248 $oneSourceCollection = $markerTemplateEngine->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
1249
1250 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] ?? [] as $className) {
1251 $hookObject = GeneralUtility::makeInstance($className);
1252 if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
1253 throw new \UnexpectedValueException(
1254 '$hookObject must implement interface ' . ContentObjectOneSourceCollectionHookInterface::class,
1255 1380007853
1256 );
1257 }
1258 $oneSourceCollection = $hookObject->getOneSourceCollection((array)$sourceRenderConfiguration, (array)$sourceConfiguration, $oneSourceCollection, $this);
1259 }
1260
1261 $sourceCollection .= $oneSourceCollection;
1262 }
1263 }
1264 }
1265 return $sourceCollection;
1266 }
1267
1268 /**
1269 * Wraps the input string in link-tags that opens the image in a new window.
1270 *
1271 * @param string $string String to wrap, probably an <img> tag
1272 * @param string|File|FileReference $imageFile The original image file
1273 * @param array $conf TypoScript properties for the "imageLinkWrap" function
1274 * @return string The input string, $string, wrapped as configured.
1275 * @see cImage()
1276 */
1277 public function imageLinkWrap($string, $imageFile, $conf)
1278 {
1279 $string = (string)$string;
1280 $enable = isset($conf['enable.']) ? $this->stdWrap($conf['enable'], $conf['enable.']) : $conf['enable'];
1281 if (!$enable) {
1282 return $string;
1283 }
1284 $content = (string)$this->typoLink($string, $conf['typolink.']);
1285 if (isset($conf['file.'])) {
1286 $imageFile = $this->stdWrap($imageFile, $conf['file.']);
1287 }
1288
1289 if ($imageFile instanceof File) {
1290 $file = $imageFile;
1291 } elseif ($imageFile instanceof FileReference) {
1292 $file = $imageFile->getOriginalFile();
1293 } else {
1294 if (MathUtility::canBeInterpretedAsInteger($imageFile)) {
1295 $file = ResourceFactory::getInstance()->getFileObject((int)$imageFile);
1296 } else {
1297 $file = ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($imageFile);
1298 }
1299 }
1300
1301 // Create imageFileLink if not created with typolink
1302 if ($content === $string) {
1303 $parameterNames = ['width', 'height', 'effects', 'bodyTag', 'title', 'wrap', 'crop'];
1304 $parameters = [];
1305 $sample = isset($conf['sample.']) ? $this->stdWrap($conf['sample'], $conf['sample.']) : $conf['sample'];
1306 if ($sample) {
1307 $parameters['sample'] = 1;
1308 }
1309 foreach ($parameterNames as $parameterName) {
1310 if (isset($conf[$parameterName . '.'])) {
1311 $conf[$parameterName] = $this->stdWrap($conf[$parameterName], $conf[$parameterName . '.']);
1312 }
1313 if (isset($conf[$parameterName]) && $conf[$parameterName]) {
1314 $parameters[$parameterName] = $conf[$parameterName];
1315 }
1316 }
1317 $parametersEncoded = base64_encode(serialize($parameters));
1318 $hmac = GeneralUtility::hmac(implode('|', [$file->getUid(), $parametersEncoded]));
1319 $params = '&md5=' . $hmac;
1320 foreach (str_split($parametersEncoded, 64) as $index => $chunk) {
1321 $params .= '&parameters' . rawurlencode('[') . $index . rawurlencode(']') . '=' . rawurlencode($chunk);
1322 }
1323 $url = $this->getTypoScriptFrontendController()->absRefPrefix . 'index.php?eID=tx_cms_showpic&file=' . $file->getUid() . $params;
1324 $directImageLink = isset($conf['directImageLink.']) ? $this->stdWrap($conf['directImageLink'], $conf['directImageLink.']) : $conf['directImageLink'];
1325 if ($directImageLink) {
1326 $imgResourceConf = [
1327 'file' => $imageFile,
1328 'file.' => $conf
1329 ];
1330 $url = $this->cObjGetSingle('IMG_RESOURCE', $imgResourceConf);
1331 if (!$url) {
1332 // If no imagemagick / gm is available
1333 $url = $imageFile;
1334 }
1335 }
1336 // Create TARGET-attribute only if the right doctype is used
1337 $target = '';
1338 $xhtmlDocType = $this->getTypoScriptFrontendController()->xhtmlDoctype;
1339 if ($xhtmlDocType !== 'xhtml_strict' && $xhtmlDocType !== 'xhtml_11') {
1340 $target = isset($conf['target.'])
1341 ? (string)$this->stdWrap($conf['target'], $conf['target.'])
1342 : (string)$conf['target'];
1343 if ($target === '') {
1344 $target = 'thePicture';
1345 }
1346 }
1347 $a1 = '';
1348 $a2 = '';
1349 $conf['JSwindow'] = isset($conf['JSwindow.']) ? $this->stdWrap($conf['JSwindow'], $conf['JSwindow.']) : $conf['JSwindow'];
1350 if ($conf['JSwindow']) {
1351 if ($conf['JSwindow.']['altUrl'] || $conf['JSwindow.']['altUrl.']) {
1352 $altUrl = isset($conf['JSwindow.']['altUrl.']) ? $this->stdWrap($conf['JSwindow.']['altUrl'], $conf['JSwindow.']['altUrl.']) : $conf['JSwindow.']['altUrl'];
1353 if ($altUrl) {
1354 $url = $altUrl . ($conf['JSwindow.']['altUrl_noDefaultParams'] ? '' : '?file=' . rawurlencode($imageFile) . $params);
1355 }
1356 }
1357
1358 $processedFile = $file->process(ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $conf);
1359 $JSwindowExpand = isset($conf['JSwindow.']['expand.']) ? $this->stdWrap($conf['JSwindow.']['expand'], $conf['JSwindow.']['expand.']) : $conf['JSwindow.']['expand'];
1360 $offset = GeneralUtility::intExplode(',', $JSwindowExpand . ',');
1361 $newWindow = isset($conf['JSwindow.']['newWindow.']) ? $this->stdWrap($conf['JSwindow.']['newWindow'], $conf['JSwindow.']['newWindow.']) : $conf['JSwindow.']['newWindow'];
1362 $onClick = 'openPic('
1363 . GeneralUtility::quoteJSvalue($this->getTypoScriptFrontendController()->baseUrlWrap($url)) . ','
1364 . '\'' . ($newWindow ? md5($url) : 'thePicture') . '\','
1365 . GeneralUtility::quoteJSvalue('width=' . ($processedFile->getProperty('width') + $offset[0])
1366 . ',height=' . ($processedFile->getProperty('height') + $offset[1]) . ',status=0,menubar=0')
1367 . '); return false;';
1368 $a1 = '<a href="' . htmlspecialchars($url) . '"'
1369 . ' onclick="' . htmlspecialchars($onClick) . '"'
1370 . ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
1371 . $this->getTypoScriptFrontendController()->ATagParams . '>';
1372 $a2 = '</a>';
1373 $this->getTypoScriptFrontendController()->setJS('openPic');
1374 } else {
1375 $conf['linkParams.']['parameter'] = $url;
1376 $string = $this->typoLink($string, $conf['linkParams.']);
1377 }
1378 if (isset($conf['stdWrap.'])) {
1379 $string = $this->stdWrap($string, $conf['stdWrap.']);
1380 }
1381 $content = $a1 . $string . $a2;
1382 }
1383 return $content;
1384 }
1385
1386 /**
1387 * Sets the SYS_LASTCHANGED timestamp if input timestamp is larger than current value.
1388 * The SYS_LASTCHANGED timestamp can be used by various caching/indexing applications to determine if the page has new content.
1389 * Therefore you should call this function with the last-changed timestamp of any element you display.
1390 *
1391 * @param int $tstamp Unix timestamp (number of seconds since 1970)
1392 * @see TypoScriptFrontendController::setSysLastChanged()
1393 */
1394 public function lastChanged($tstamp)
1395 {
1396 $tstamp = (int)$tstamp;
1397 $tsfe = $this->getTypoScriptFrontendController();
1398 if ($tstamp > (int)$tsfe->register['SYS_LASTCHANGED']) {
1399 $tsfe->register['SYS_LASTCHANGED'] = $tstamp;
1400 }
1401 }
1402
1403 /**
1404 * Wraps the input string by the $wrap value and implements the "linkWrap" data type as well.
1405 * The "linkWrap" data type means that this function will find any integer encapsulated in {} (curly braces) in the first wrap part and substitute it with the corresponding page uid from the rootline where the found integer is pointing to the key in the rootline. See link below.
1406 *
1407 * @param string $content Input string
1408 * @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string
1409 * @return string Wrapped output string
1410 * @see wrap(), cImage(), FILE()
1411 */
1412 public function linkWrap($content, $wrap)
1413 {
1414 $wrapArr = explode('|', $wrap);
1415 if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
1416 $uid = $this->getTypoScriptFrontendController()->tmpl->rootLine[$reg[1]]['uid'] ?? null;
1417 if ($uid) {
1418 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
1419 }
1420 }
1421 return trim($wrapArr[0] ?? '') . $content . trim($wrapArr[1] ?? '');
1422 }
1423
1424 /**
1425 * An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element.
1426 * From the $conf array it implements the properties "altText", "titleText" and "longdescURL"
1427 *
1428 * @param array $conf TypoScript configuration properties
1429 * @param bool $longDesc If set, the longdesc attribute will be generated - must only be used for img elements!
1430 * @return string Parameter string containing alt and title parameters (if any)
1431 * @see IMGTEXT(), FILE(), FORM(), cImage(), filelink()
1432 */
1433 public function getAltParam($conf, $longDesc = true)
1434 {
1435 $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
1436 $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
1437 if (isset($conf['longdescURL.']) && $this->getTypoScriptFrontendController()->config['config']['doctype'] !== 'html5') {
1438 $longDescUrl = $this->typoLink_URL($conf['longdescURL.']);
1439 } else {
1440 $longDescUrl = trim($conf['longdescURL']);
1441 }
1442 $longDescUrl = strip_tags($longDescUrl);
1443
1444 // "alt":
1445 $altParam = ' alt="' . htmlspecialchars($altText) . '"';
1446 // "title":
1447 $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
1448 // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
1449 if ($titleText || $emptyTitleHandling === 'keepEmpty') {
1450 $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
1451 } elseif (!$titleText && $emptyTitleHandling === 'useAlt') {
1452 $altParam .= ' title="' . htmlspecialchars($altText) . '"';
1453 }
1454 // "longDesc" URL
1455 if ($longDesc && !empty($longDescUrl)) {
1456 $altParam .= ' longdesc="' . htmlspecialchars($longDescUrl) . '"';
1457 }
1458 return $altParam;
1459 }
1460
1461 /**
1462 * An abstraction method to add parameters to an A tag.
1463 * Uses the ATagParams property.
1464 *
1465 * @param array $conf TypoScript configuration properties
1466 * @param bool|int $addGlobal If set, will add the global config.ATagParams to the link
1467 * @return string String containing the parameters to the A tag (if non empty, with a leading space)
1468 * @see IMGTEXT(), filelink(), makelinks(), typolink()
1469 */
1470 public function getATagParams($conf, $addGlobal = 1)
1471 {
1472 $aTagParams = '';
1473 if ($conf['ATagParams.'] ?? false) {
1474 $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
1475 } elseif ($conf['ATagParams'] ?? false) {
1476 $aTagParams = ' ' . $conf['ATagParams'];
1477 }
1478 if ($addGlobal) {
1479 $aTagParams = ' ' . trim($this->getTypoScriptFrontendController()->ATagParams . $aTagParams);
1480 }
1481 // Extend params
1482 $_params = [
1483 'conf' => &$conf,
1484 'aTagParams' => &$aTagParams
1485 ];
1486 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] ?? [] as $className) {
1487 $processor = & GeneralUtility::makeInstance($className);
1488 $aTagParams = $processor->process($_params, $this);
1489 }
1490
1491 $aTagParams = trim($aTagParams);
1492 if (!empty($aTagParams)) {
1493 $aTagParams = ' ' . $aTagParams;
1494 }
1495
1496 return $aTagParams;
1497 }
1498
1499 /**
1500 * All extension links should ask this function for additional properties to their tags.
1501 * Designed to add for instance an "onclick" property for site tracking systems.
1502 *
1503 * @param string $URL URL of the website
1504 * @param string $TYPE
1505 * @return string The additional tag properties
1506 */
1507 public function extLinkATagParams($URL, $TYPE)
1508 {
1509 $out = '';
1510 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler'])) {
1511 $extLinkATagParamsHandler = GeneralUtility::makeInstance($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
1512 if (method_exists($extLinkATagParamsHandler, 'main')) {
1513 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
1514 }
1515 }
1516 return trim($out) ? ' ' . trim($out) : '';
1517 }
1518
1519 /***********************************************
1520 *
1521 * HTML template processing functions
1522 *
1523 ***********************************************/
1524
1525 /**
1526 * Sets the current file object during iterations over files.
1527 *
1528 * @param File $fileObject The file object.
1529 */
1530 public function setCurrentFile($fileObject)
1531 {
1532 $this->currentFile = $fileObject;
1533 }
1534
1535 /**
1536 * Gets the current file object during iterations over files.
1537 *
1538 * @return File The current file object.
1539 */
1540 public function getCurrentFile()
1541 {
1542 return $this->currentFile;
1543 }
1544
1545 /***********************************************
1546 *
1547 * "stdWrap" + sub functions
1548 *
1549 ***********************************************/
1550 /**
1551 * The "stdWrap" function. This is the implementation of what is known as "stdWrap properties" in TypoScript.
1552 * Basically "stdWrap" performs some processing of a value based on properties in the input $conf array(holding the TypoScript "stdWrap properties")
1553 * See the link below for a complete list of properties and what they do. The order of the table with properties found in TSref (the link) follows the actual order of implementation in this function.
1554 *
1555 * If $this->alternativeData is an array it's used instead of the $this->data array in ->getData
1556 *
1557 * @param string $content Input value undergoing processing in this function. Possibly substituted by other values fetched from another source.
1558 * @param array $conf TypoScript "stdWrap properties".
1559 * @return string The processed input value
1560 */
1561 public function stdWrap($content = '', $conf = [])
1562 {
1563 $content = (string)$content;
1564 // If there is any hook object, activate all of the process and override functions.
1565 // The hook interface ContentObjectStdWrapHookInterface takes care that all 4 methods exist.
1566 if ($this->stdWrapHookObjects) {
1567 $conf['stdWrapPreProcess'] = 1;
1568 $conf['stdWrapOverride'] = 1;
1569 $conf['stdWrapProcess'] = 1;
1570 $conf['stdWrapPostProcess'] = 1;
1571 }
1572
1573 if (!is_array($conf) || !$conf) {
1574 return $content;
1575 }
1576
1577 // Cache handling
1578 if (isset($conf['cache.']) && is_array($conf['cache.'])) {
1579 $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
1580 $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
1581 $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
1582 $conf['cacheRead'] = 1;
1583 $conf['cacheStore'] = 1;
1584 }
1585 // The configuration is sorted and filtered by intersection with the defined stdWrapOrder.
1586 $sortedConf = array_keys(array_intersect_key($this->stdWrapOrder, $conf));
1587 // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
1588 $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
1589 // Additional Array to check whether a function has already been executed
1590 $isExecuted = [];
1591 // Additional switch to make sure 'required', 'if' and 'fieldRequired'
1592 // will still stop rendering immediately in case they return FALSE
1593 $this->stdWrapRecursionLevel++;
1594 $this->stopRendering[$this->stdWrapRecursionLevel] = false;
1595 // execute each function in the predefined order
1596 foreach ($sortedConf as $stdWrapName) {
1597 // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
1598 if ((!isset($isExecuted[$stdWrapName]) || !$isExecuted[$stdWrapName]) && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
1599 $functionName = rtrim($stdWrapName, '.');
1600 $functionProperties = $functionName . '.';
1601 $functionType = $this->stdWrapOrder[$functionName] ?? null;
1602 // If there is any code on the next level, check if it contains "official" stdWrap functions
1603 // if yes, execute them first - will make each function stdWrap aware
1604 // so additional stdWrap calls within the functions can be removed, since the result will be the same
1605 if (!empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
1606 if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
1607 $conf[$functionName] = $this->stdWrap($conf[$functionName] ?? '', $conf[$functionProperties] ?? []);
1608 }
1609 }
1610 // Check if key is still containing something, since it might have been changed by next level stdWrap before
1611 if ((isset($conf[$functionName]) || $conf[$functionProperties])
1612 && ($functionType !== 'boolean' || $conf[$functionName])
1613 ) {
1614 // Get just that part of $conf that is needed for the particular function
1615 $singleConf = [
1616 $functionName => $conf[$functionName] ?? null,
1617 $functionProperties => $conf[$functionProperties] ?? null
1618 ];
1619 // Hand over the whole $conf array to the stdWrapHookObjects
1620 if ($functionType === 'hook') {
1621 $singleConf = $conf;
1622 }
1623 // Add both keys - with and without the dot - to the set of executed functions
1624 $isExecuted[$functionName] = true;
1625 $isExecuted[$functionProperties] = true;
1626 // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
1627 $functionName = 'stdWrap_' . $functionName;
1628 $content = $this->{$functionName}($content, $singleConf);
1629 } elseif ($functionType === 'boolean' && !$conf[$functionName]) {
1630 $isExecuted[$functionName] = true;
1631 $isExecuted[$functionProperties] = true;
1632 }
1633 }
1634 }
1635 unset($this->stopRendering[$this->stdWrapRecursionLevel]);
1636 $this->stdWrapRecursionLevel--;
1637
1638 return $content;
1639 }
1640
1641 /**
1642 * Gets a configuration value by passing them through stdWrap first and taking a default value if stdWrap doesn't yield a result.
1643 *
1644 * @param string $key The config variable key (from TS array).
1645 * @param array $config The TypoScript array.
1646 * @param string $defaultValue Optional default value.
1647 * @return string Value of the config variable
1648 */
1649 public function stdWrapValue($key, array $config, $defaultValue = '')
1650 {
1651 if (isset($config[$key])) {
1652 if (!isset($config[$key . '.'])) {
1653 return $config[$key];
1654 }
1655 } elseif (isset($config[$key . '.'])) {
1656 $config[$key] = '';
1657 } else {
1658 return $defaultValue;
1659 }
1660 $stdWrapped = $this->stdWrap($config[$key], $config[$key . '.']);
1661 return $stdWrapped ?: $defaultValue;
1662 }
1663
1664 /**
1665 * stdWrap pre process hook
1666 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
1667 * this hook will execute functions before any other stdWrap function can modify anything
1668 *
1669 * @param string $content Input value undergoing processing in these functions.
1670 * @param array $conf All stdWrap properties, not just the ones for a particular function.
1671 * @return string The processed input value
1672 */
1673 public function stdWrap_stdWrapPreProcess($content = '', $conf = [])
1674 {
1675 foreach ($this->stdWrapHookObjects as $hookObject) {
1676 /** @var ContentObjectStdWrapHookInterface $hookObject */
1677 $content = $hookObject->stdWrapPreProcess($content, $conf, $this);
1678 }
1679 return $content;
1680 }
1681
1682 /**
1683 * Check if content was cached before (depending on the given cache key)
1684 *
1685 * @param string $content Input value undergoing processing in these functions.
1686 * @param array $conf All stdWrap properties, not just the ones for a particular function.
1687 * @return string The processed input value
1688 */
1689 public function stdWrap_cacheRead($content = '', $conf = [])
1690 {
1691 if (!isset($conf['cache.'])) {
1692 return $content;
1693 }
1694 $result = $this->getFromCache($conf['cache.']);
1695 return $result === false ? $content : $result;
1696 }
1697
1698 /**
1699 * Add tags to page cache (comma-separated list)
1700 *
1701 * @param string $content Input value undergoing processing in these functions.
1702 * @param array $conf All stdWrap properties, not just the ones for a particular function.
1703 * @return string The processed input value
1704 */
1705 public function stdWrap_addPageCacheTags($content = '', $conf = [])
1706 {
1707 $tags = isset($conf['addPageCacheTags.'])
1708 ? $this->stdWrap($conf['addPageCacheTags'], $conf['addPageCacheTags.'])
1709 : $conf['addPageCacheTags'];
1710 if (!empty($tags)) {
1711 $cacheTags = GeneralUtility::trimExplode(',', $tags, true);
1712 $this->getTypoScriptFrontendController()->addCacheTags($cacheTags);
1713 }
1714 return $content;
1715 }
1716
1717 /**
1718 * setContentToCurrent
1719 * actually it just does the contrary: Sets the value of 'current' based on current content
1720 *
1721 * @param string $content Input value undergoing processing in this function.
1722 * @return string The processed input value
1723 */
1724 public function stdWrap_setContentToCurrent($content = '')
1725 {
1726 $this->data[$this->currentValKey] = $content;
1727 return $content;
1728 }
1729
1730 /**
1731 * setCurrent
1732 * Sets the value of 'current' based on the outcome of stdWrap operations
1733 *
1734 * @param string $content Input value undergoing processing in this function.
1735 * @param array $conf stdWrap properties for setCurrent.
1736 * @return string The processed input value
1737 */
1738 public function stdWrap_setCurrent($content = '', $conf = [])
1739 {
1740 $this->data[$this->currentValKey] = $conf['setCurrent'] ?? null;
1741 return $content;
1742 }
1743
1744 /**
1745 * lang
1746 * Translates content based on the language currently used by the FE
1747 *
1748 * @param string $content Input value undergoing processing in this function.
1749 * @param array $conf stdWrap properties for lang.
1750 * @return string The processed input value
1751 */
1752 public function stdWrap_lang($content = '', $conf = [])
1753 {
1754 $tsfe = $this->getTypoScriptFrontendController();
1755 if (
1756 isset($conf['lang.'])
1757 && isset($tsfe->config['config']['language'])
1758 && $tsfe->config['config']['language']
1759 && isset($conf['lang.'][$tsfe->config['config']['language']])
1760 ) {
1761 $content = $conf['lang.'][$tsfe->config['config']['language']];
1762 }
1763 return $content;
1764 }
1765
1766 /**
1767 * data
1768 * Gets content from different sources based on getText functions, makes use of alternativeData, when set
1769 *
1770 * @param string $content Input value undergoing processing in this function.
1771 * @param array $conf stdWrap properties for data.
1772 * @return string The processed input value
1773 */
1774 public function stdWrap_data($content = '', $conf = [])
1775 {
1776 $content = $this->getData($conf['data'], is_array($this->alternativeData) ? $this->alternativeData : $this->data);
1777 // This must be unset directly after
1778 $this->alternativeData = '';
1779 return $content;
1780 }
1781
1782 /**
1783 * field
1784 * Gets content from a DB field
1785 *
1786 * @param string $content Input value undergoing processing in this function.
1787 * @param array $conf stdWrap properties for field.
1788 * @return string The processed input value
1789 */
1790 public function stdWrap_field($content = '', $conf = [])
1791 {
1792 return $this->getFieldVal($conf['field']);
1793 }
1794
1795 /**
1796 * current
1797 * Gets content that has been perviously set as 'current'
1798 * Can be set via setContentToCurrent or setCurrent or will be set automatically i.e. inside the split function
1799 *
1800 * @param string $content Input value undergoing processing in this function.
1801 * @param array $conf stdWrap properties for current.
1802 * @return string The processed input value
1803 */
1804 public function stdWrap_current($content = '', $conf = [])
1805 {
1806 return $this->data[$this->currentValKey];
1807 }
1808
1809 /**
1810 * cObject
1811 * Will replace the content with the value of an official TypoScript cObject
1812 * like TEXT, COA, HMENU
1813 *
1814 * @param string $content Input value undergoing processing in this function.
1815 * @param array $conf stdWrap properties for cObject.
1816 * @return string The processed input value
1817 */
1818 public function stdWrap_cObject($content = '', $conf = [])
1819 {
1820 return $this->cObjGetSingle($conf['cObject'] ?? '', $conf['cObject.'] ?? [], '/stdWrap/.cObject');
1821 }
1822
1823 /**
1824 * numRows
1825 * Counts the number of returned records of a DB operation
1826 * makes use of select internally
1827 *
1828 * @param string $content Input value undergoing processing in this function.
1829 * @param array $conf stdWrap properties for numRows.
1830 * @return string The processed input value
1831 */
1832 public function stdWrap_numRows($content = '', $conf = [])
1833 {
1834 return $this->numRows($conf['numRows.']);
1835 }
1836
1837 /**
1838 * filelist
1839 * Will create a list of files based on some additional parameters
1840 *
1841 * @param string $content Input value undergoing processing in this function.
1842 * @param array $conf stdWrap properties for filelist.
1843 * @return string The processed input value
1844 * @deprecated since TYPO3 v9.5, will be removed in TYPO3 v10.0. Use cObject FILES instead.
1845 */
1846 public function stdWrap_filelist($content = '', $conf = [])
1847 {
1848 return $this->filelist($conf['filelist'], true);
1849 }
1850
1851 /**
1852 * preUserFunc
1853 * Will execute a user public function before the content will be modified by any other stdWrap function
1854 *
1855 * @param string $content Input value undergoing processing in this function.
1856 * @param array $conf stdWrap properties for preUserFunc.
1857 * @return string The processed input value
1858 */
1859 public function stdWrap_preUserFunc($content = '', $conf = [])
1860 {
1861 return $this->callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'], $content);
1862 }
1863
1864 /**
1865 * stdWrap override hook
1866 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
1867 * this hook will execute functions on existing content but still before the content gets modified or replaced
1868 *
1869 * @param string $content Input value undergoing processing in these functions.
1870 * @param array $conf All stdWrap properties, not just the ones for a particular function.
1871 * @return string The processed input value
1872 */
1873 public function stdWrap_stdWrapOverride($content = '', $conf = [])
1874 {
1875 foreach ($this->stdWrapHookObjects as $hookObject) {
1876 /** @var ContentObjectStdWrapHookInterface $hookObject */
1877 $content = $hookObject->stdWrapOverride($content, $conf, $this);
1878 }
1879 return $content;
1880 }
1881
1882 /**
1883 * override
1884 * Will override the current value of content with its own value'
1885 *
1886 * @param string $content Input value undergoing processing in this function.
1887 * @param array $conf stdWrap properties for override.
1888 * @return string The processed input value
1889 */
1890 public function stdWrap_override($content = '', $conf = [])
1891 {
1892 if (trim($conf['override'] ?? false)) {
1893 $content = $conf['override'];
1894 }
1895 return $content;
1896 }
1897
1898 /**
1899 * preIfEmptyListNum
1900 * Gets a value off a CSV list before the following ifEmpty check
1901 * Makes sure that the result of ifEmpty will be TRUE in case the CSV does not contain a value at the position given by preIfEmptyListNum
1902 *
1903 * @param string $content Input value undergoing processing in this function.
1904 * @param array $conf stdWrap properties for preIfEmptyListNum.
1905 * @return string The processed input value
1906 */
1907 public function stdWrap_preIfEmptyListNum($content = '', $conf = [])
1908 {
1909 return $this->listNum($content, $conf['preIfEmptyListNum'] ?? null, $conf['preIfEmptyListNum.']['splitChar'] ?? null);
1910 }
1911
1912 /**
1913 * ifNull
1914 * Will set content to a replacement value in case the value of content is NULL
1915 *
1916 * @param string|null $content Input value undergoing processing in this function.
1917 * @param array $conf stdWrap properties for ifNull.
1918 * @return string The processed input value
1919 */
1920 public function stdWrap_ifNull($content = '', $conf = [])
1921 {
1922 return $content ?? $conf['ifNull'];
1923 }
1924
1925 /**
1926 * ifEmpty
1927 * Will set content to a replacement value in case the trimmed value of content returns FALSE
1928 * 0 (zero) will be replaced as well
1929 *
1930 * @param string $content Input value undergoing processing in this function.
1931 * @param array $conf stdWrap properties for ifEmpty.
1932 * @return string The processed input value
1933 */
1934 public function stdWrap_ifEmpty($content = '', $conf = [])
1935 {
1936 if (!trim($content)) {
1937 $content = $conf['ifEmpty'];
1938 }
1939 return $content;
1940 }
1941
1942 /**
1943 * ifBlank
1944 * Will set content to a replacement value in case the trimmed value of content has no length
1945 * 0 (zero) will not be replaced
1946 *
1947 * @param string $content Input value undergoing processing in this function.
1948 * @param array $conf stdWrap properties for ifBlank.
1949 * @return string The processed input value
1950 */
1951 public function stdWrap_ifBlank($content = '', $conf = [])
1952 {
1953 if (trim($content) === '') {
1954 $content = $conf['ifBlank'];
1955 }
1956 return $content;
1957 }
1958
1959 /**
1960 * listNum
1961 * Gets a value off a CSV list after ifEmpty check
1962 * Might return an empty value in case the CSV does not contain a value at the position given by listNum
1963 * Use preIfEmptyListNum to avoid that behaviour
1964 *
1965 * @param string $content Input value undergoing processing in this function.
1966 * @param array $conf stdWrap properties for listNum.
1967 * @return string The processed input value
1968 */
1969 public function stdWrap_listNum($content = '', $conf = [])
1970 {
1971 return $this->listNum($content, $conf['listNum'] ?? null, $conf['listNum.']['splitChar'] ?? null);
1972 }
1973
1974 /**
1975 * trim
1976 * Cuts off any whitespace at the beginning and the end of the content
1977 *
1978 * @param string $content Input value undergoing processing in this function.
1979 * @return string The processed input value
1980 */
1981 public function stdWrap_trim($content = '')
1982 {
1983 return trim($content);
1984 }
1985
1986 /**
1987 * strPad
1988 * Will return a string padded left/right/on both sides, based on configuration given as stdWrap properties
1989 *
1990 * @param string $content Input value undergoing processing in this function.
1991 * @param array $conf stdWrap properties for strPad.
1992 * @return string The processed input value
1993 */
1994 public function stdWrap_strPad($content = '', $conf = [])
1995 {
1996 // Must specify a length in conf for this to make sense
1997 $length = 0;
1998 // Padding with space is PHP-default
1999 $padWith = ' ';
2000 // Padding on the right side is PHP-default
2001 $padType = STR_PAD_RIGHT;
2002 if (!empty($conf['strPad.']['length'])) {
2003 $length = isset($conf['strPad.']['length.']) ? $this->stdWrap($conf['strPad.']['length'], $conf['strPad.']['length.']) : $conf['strPad.']['length'];
2004 $length = (int)$length;
2005 }
2006 if (isset($conf['strPad.']['padWith']) && (string)$conf['strPad.']['padWith'] !== '') {
2007 $padWith = isset($conf['strPad.']['padWith.']) ? $this->stdWrap($conf['strPad.']['padWith'], $conf['strPad.']['padWith.']) : $conf['strPad.']['padWith'];
2008 }
2009 if (!empty($conf['strPad.']['type'])) {
2010 $type = isset($conf['strPad.']['type.']) ? $this->stdWrap($conf['strPad.']['type'], $conf['strPad.']['type.']) : $conf['strPad.']['type'];
2011 if (strtolower($type) === 'left') {
2012 $padType = STR_PAD_LEFT;
2013 } elseif (strtolower($type) === 'both') {
2014 $padType = STR_PAD_BOTH;
2015 }
2016 }
2017 return str_pad($content, $length, $padWith, $padType);
2018 }
2019
2020 /**
2021 * stdWrap
2022 * A recursive call of the stdWrap function set
2023 * This enables the user to execute stdWrap functions in another than the predefined order
2024 * It modifies the content, not the property
2025 * while the new feature of chained stdWrap functions modifies the property and not the content
2026 *
2027 * @param string $content Input value undergoing processing in this function.
2028 * @param array $conf stdWrap properties for stdWrap.
2029 * @return string The processed input value
2030 */
2031 public function stdWrap_stdWrap($content = '', $conf = [])
2032 {
2033 return $this->stdWrap($content, $conf['stdWrap.']);
2034 }
2035
2036 /**
2037 * stdWrap process hook
2038 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2039 * this hook executes functions directly after the recursive stdWrap function call but still before the content gets modified
2040 *
2041 * @param string $content Input value undergoing processing in these functions.
2042 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2043 * @return string The processed input value
2044 */
2045 public function stdWrap_stdWrapProcess($content = '', $conf = [])
2046 {
2047 foreach ($this->stdWrapHookObjects as $hookObject) {
2048 /** @var ContentObjectStdWrapHookInterface $hookObject */
2049 $content = $hookObject->stdWrapProcess($content, $conf, $this);
2050 }
2051 return $content;
2052 }
2053
2054 /**
2055 * required
2056 * Will immediately stop rendering and return an empty value
2057 * when there is no content at this point
2058 *
2059 * @param string $content Input value undergoing processing in this function.
2060 * @return string The processed input value
2061 */
2062 public function stdWrap_required($content = '')
2063 {
2064 if ((string)$content === '') {
2065 $content = '';
2066 $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2067 }
2068 return $content;
2069 }
2070
2071 /**
2072 * if
2073 * Will immediately stop rendering and return an empty value
2074 * when the result of the checks returns FALSE
2075 *
2076 * @param string $content Input value undergoing processing in this function.
2077 * @param array $conf stdWrap properties for if.
2078 * @return string The processed input value
2079 */
2080 public function stdWrap_if($content = '', $conf = [])
2081 {
2082 if (empty($conf['if.']) || $this->checkIf($conf['if.'])) {
2083 return $content;
2084 }
2085 $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2086 return '';
2087 }
2088
2089 /**
2090 * fieldRequired
2091 * Will immediately stop rendering and return an empty value
2092 * when there is no content in the field given by fieldRequired
2093 *
2094 * @param string $content Input value undergoing processing in this function.
2095 * @param array $conf stdWrap properties for fieldRequired.
2096 * @return string The processed input value
2097 */
2098 public function stdWrap_fieldRequired($content = '', $conf = [])
2099 {
2100 if (!trim($this->data[$conf['fieldRequired'] ?? null] ?? '')) {
2101 $content = '';
2102 $this->stopRendering[$this->stdWrapRecursionLevel] = true;
2103 }
2104 return $content;
2105 }
2106
2107 /**
2108 * stdWrap csConv: Converts the input to UTF-8
2109 *
2110 * The character set of the input must be specified. Returns the input if
2111 * matters go wrong, for example if an invalid character set is given.
2112 *
2113 * @param string $content The string to convert.
2114 * @param array $conf stdWrap properties for csConv.
2115 * @return string The processed input.
2116 */
2117 public function stdWrap_csConv($content = '', $conf = [])
2118 {
2119 if (!empty($conf['csConv'])) {
2120 $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['csConv'])));
2121 return $output !== false && $output !== '' ? $output : $content;
2122 }
2123 return $content;
2124 }
2125
2126 /**
2127 * parseFunc
2128 * Will parse the content based on functions given as stdWrap properties
2129 * Heavily used together with RTE based content
2130 *
2131 * @param string $content Input value undergoing processing in this function.
2132 * @param array $conf stdWrap properties for parseFunc.
2133 * @return string The processed input value
2134 */
2135 public function stdWrap_parseFunc($content = '', $conf = [])
2136 {
2137 return $this->parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
2138 }
2139
2140 /**
2141 * HTMLparser
2142 * Will parse HTML content based on functions given as stdWrap properties
2143 * Heavily used together with RTE based content
2144 *
2145 * @param string $content Input value undergoing processing in this function.
2146 * @param array $conf stdWrap properties for HTMLparser.
2147 * @return string The processed input value
2148 */
2149 public function stdWrap_HTMLparser($content = '', $conf = [])
2150 {
2151 if (isset($conf['HTMLparser.']) && is_array($conf['HTMLparser.'])) {
2152 $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
2153 }
2154 return $content;
2155 }
2156
2157 /**
2158 * split
2159 * Will split the content by a given token and treat the results separately
2160 * Automatically fills 'current' with a single result
2161 *
2162 * @param string $content Input value undergoing processing in this function.
2163 * @param array $conf stdWrap properties for split.
2164 * @return string The processed input value
2165 */
2166 public function stdWrap_split($content = '', $conf = [])
2167 {
2168 return $this->splitObj($content, $conf['split.']);
2169 }
2170
2171 /**
2172 * replacement
2173 * Will execute replacements on the content (optionally with preg-regex)
2174 *
2175 * @param string $content Input value undergoing processing in this function.
2176 * @param array $conf stdWrap properties for replacement.
2177 * @return string The processed input value
2178 */
2179 public function stdWrap_replacement($content = '', $conf = [])
2180 {
2181 return $this->replacement($content, $conf['replacement.']);
2182 }
2183
2184 /**
2185 * prioriCalc
2186 * Will use the content as a mathematical term and calculate the result
2187 * Can be set to 1 to just get a calculated value or 'intval' to get the integer of the result
2188 *
2189 * @param string $content Input value undergoing processing in this function.
2190 * @param array $conf stdWrap properties for prioriCalc.
2191 * @return string The processed input value
2192 */
2193 public function stdWrap_prioriCalc($content = '', $conf = [])
2194 {
2195 $content = MathUtility::calculateWithParentheses($content);
2196 if (!empty($conf['prioriCalc']) && $conf['prioriCalc'] === 'intval') {
2197 $content = (int)$content;
2198 }
2199 return $content;
2200 }
2201
2202 /**
2203 * char
2204 * Returns a one-character string containing the character specified by ascii code.
2205 *
2206 * Reliable results only for character codes in the integer range 0 - 127.
2207 *
2208 * @see http://php.net/manual/en/function.chr.php
2209 * @param string $content Input value undergoing processing in this function.
2210 * @param array $conf stdWrap properties for char.
2211 * @return string The processed input value
2212 */
2213 public function stdWrap_char($content = '', $conf = [])
2214 {
2215 return chr((int)$conf['char']);
2216 }
2217
2218 /**
2219 * intval
2220 * Will return an integer value of the current content
2221 *
2222 * @param string $content Input value undergoing processing in this function.
2223 * @return string The processed input value
2224 */
2225 public function stdWrap_intval($content = '')
2226 {
2227 return (int)$content;
2228 }
2229
2230 /**
2231 * Will return a hashed value of the current content
2232 *
2233 * @param string $content Input value undergoing processing in this function.
2234 * @param array $conf stdWrap properties for hash.
2235 * @return string The processed input value
2236 * @link http://php.net/manual/de/function.hash-algos.php for a list of supported hash algorithms
2237 */
2238 public function stdWrap_hash($content = '', array $conf = [])
2239 {
2240 $algorithm = isset($conf['hash.']) ? $this->stdWrap($conf['hash'], $conf['hash.']) : $conf['hash'];
2241 if (function_exists('hash') && in_array($algorithm, hash_algos())) {
2242 return hash($algorithm, $content);
2243 }
2244 // Non-existing hashing algorithm
2245 return '';
2246 }
2247
2248 /**
2249 * stdWrap_round will return a rounded number with ceil(), floor() or round(), defaults to round()
2250 * Only the english number format is supported . (dot) as decimal point
2251 *
2252 * @param string $content Input value undergoing processing in this function.
2253 * @param array $conf stdWrap properties for round.
2254 * @return string The processed input value
2255 */
2256 public function stdWrap_round($content = '', $conf = [])
2257 {
2258 return $this->round($content, $conf['round.']);
2259 }
2260
2261 /**
2262 * numberFormat
2263 * Will return a formatted number based on configuration given as stdWrap properties
2264 *
2265 * @param string $content Input value undergoing processing in this function.
2266 * @param array $conf stdWrap properties for numberFormat.
2267 * @return string The processed input value
2268 */
2269 public function stdWrap_numberFormat($content = '', $conf = [])
2270 {
2271 return $this->numberFormat($content, $conf['numberFormat.'] ?? []);
2272 }
2273
2274 /**
2275 * expandList
2276 * Will return a formatted number based on configuration given as stdWrap properties
2277 *
2278 * @param string $content Input value undergoing processing in this function.
2279 * @return string The processed input value
2280 */
2281 public function stdWrap_expandList($content = '')
2282 {
2283 return GeneralUtility::expandList($content);
2284 }
2285
2286 /**
2287 * date
2288 * Will return a formatted date based on configuration given according to PHP date/gmdate properties
2289 * Will return gmdate when the property GMT returns TRUE
2290 *
2291 * @param string $content Input value undergoing processing in this function.
2292 * @param array $conf stdWrap properties for date.
2293 * @return string The processed input value
2294 */
2295 public function stdWrap_date($content = '', $conf = [])
2296 {
2297 // Check for zero length string to mimic default case of date/gmdate.
2298 $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
2299 $content = !empty($conf['date.']['GMT']) ? gmdate($conf['date'] ?? null, $content) : date($conf['date'] ?? null, $content);
2300 return $content;
2301 }
2302
2303 /**
2304 * strftime
2305 * Will return a formatted date based on configuration given according to PHP strftime/gmstrftime properties
2306 * Will return gmstrftime when the property GMT returns TRUE
2307 *
2308 * @param string $content Input value undergoing processing in this function.
2309 * @param array $conf stdWrap properties for strftime.
2310 * @return string The processed input value
2311 */
2312 public function stdWrap_strftime($content = '', $conf = [])
2313 {
2314 // Check for zero length string to mimic default case of strtime/gmstrftime
2315 $content = (string)$content === '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
2316 $content = (isset($conf['strftime.']['GMT']) && $conf['strftime.']['GMT'])
2317 ? gmstrftime($conf['strftime'] ?? null, $content)
2318 : strftime($conf['strftime'] ?? null, $content);
2319 if (!empty($conf['strftime.']['charset'])) {
2320 $output = mb_convert_encoding($content, 'utf-8', trim(strtolower($conf['strftime.']['charset'])));
2321 return $output ?: $content;
2322 }
2323 return $content;
2324 }
2325
2326 /**
2327 * strtotime
2328 * Will return a timestamp based on configuration given according to PHP strtotime
2329 *
2330 * @param string $content Input value undergoing processing in this function.
2331 * @param array $conf stdWrap properties for strtotime.
2332 * @return string The processed input value
2333 */
2334 public function stdWrap_strtotime($content = '', $conf = [])
2335 {
2336 if ($conf['strtotime'] !== '1') {
2337 $content .= ' ' . $conf['strtotime'];
2338 }
2339 return strtotime($content, $GLOBALS['EXEC_TIME']);
2340 }
2341
2342 /**
2343 * age
2344 * Will return the age of a given timestamp based on configuration given by stdWrap properties
2345 *
2346 * @param string $content Input value undergoing processing in this function.
2347 * @param array $conf stdWrap properties for age.
2348 * @return string The processed input value
2349 */
2350 public function stdWrap_age($content = '', $conf = [])
2351 {
2352 return $this->calcAge((int)($GLOBALS['EXEC_TIME'] ?? 0) - (int)$content, $conf['age'] ?? null);
2353 }
2354
2355 /**
2356 * case
2357 * Will transform the content to be upper or lower case only
2358 * Leaves HTML tags untouched
2359 *
2360 * @param string $content Input value undergoing processing in this function.
2361 * @param array $conf stdWrap properties for case.
2362 * @return string The processed input value
2363 */
2364 public function stdWrap_case($content = '', $conf = [])
2365 {
2366 return $this->HTMLcaseshift($content, $conf['case']);
2367 }
2368
2369 /**
2370 * bytes
2371 * Will return the size of a given number in Bytes *
2372 *
2373 * @param string $content Input value undergoing processing in this function.
2374 * @param array $conf stdWrap properties for bytes.
2375 * @return string The processed input value
2376 */
2377 public function stdWrap_bytes($content = '', $conf = [])
2378 {
2379 return GeneralUtility::formatSize($content, $conf['bytes.']['labels'], $conf['bytes.']['base']);
2380 }
2381
2382 /**
2383 * substring
2384 * Will return a substring based on position information given by stdWrap properties
2385 *
2386 * @param string $content Input value undergoing processing in this function.
2387 * @param array $conf stdWrap properties for substring.
2388 * @return string The processed input value
2389 */
2390 public function stdWrap_substring($content = '', $conf = [])
2391 {
2392 return $this->substring($content, $conf['substring']);
2393 }
2394
2395 /**
2396 * cropHTML
2397 * Crops content to a given size while leaving HTML tags untouched
2398 *
2399 * @param string $content Input value undergoing processing in this function.
2400 * @param array $conf stdWrap properties for cropHTML.
2401 * @return string The processed input value
2402 */
2403 public function stdWrap_cropHTML($content = '', $conf = [])
2404 {
2405 return $this->cropHTML($content, $conf['cropHTML'] ?? '');
2406 }
2407
2408 /**
2409 * stripHtml
2410 * Copmletely removes HTML tags from content
2411 *
2412 * @param string $content Input value undergoing processing in this function.
2413 * @return string The processed input value
2414 */
2415 public function stdWrap_stripHtml($content = '')
2416 {
2417 return strip_tags($content);
2418 }
2419
2420 /**
2421 * crop
2422 * Crops content to a given size without caring about HTML tags
2423 *
2424 * @param string $content Input value undergoing processing in this function.
2425 * @param array $conf stdWrap properties for crop.
2426 * @return string The processed input value
2427 */
2428 public function stdWrap_crop($content = '', $conf = [])
2429 {
2430 return $this->crop($content, $conf['crop']);
2431 }
2432
2433 /**
2434 * rawUrlEncode
2435 * Encodes content to be used within URLs
2436 *
2437 * @param string $content Input value undergoing processing in this function.
2438 * @return string The processed input value
2439 */
2440 public function stdWrap_rawUrlEncode($content = '')
2441 {
2442 return rawurlencode($content);
2443 }
2444
2445 /**
2446 * htmlSpecialChars
2447 * Transforms HTML tags to readable text by replacing special characters with their HTML entity
2448 * When preserveEntities returns TRUE, existing entities will be left untouched
2449 *
2450 * @param string $content Input value undergoing processing in this function.
2451 * @param array $conf stdWrap properties for htmlSpecalChars.
2452 * @return string The processed input value
2453 */
2454 public function stdWrap_htmlSpecialChars($content = '', $conf = [])
2455 {
2456 if (!empty($conf['htmlSpecialChars.']['preserveEntities'])) {
2457 $content = htmlspecialchars($content, ENT_COMPAT, 'UTF-8', false);
2458 } else {
2459 $content = htmlspecialchars($content);
2460 }
2461 return $content;
2462 }
2463
2464 /**
2465 * encodeForJavaScriptValue
2466 * Escapes content to be used inside JavaScript strings. No quotes are added around the value
2467 * as this can easily be done in TypoScript
2468 *
2469 * @param string $content Input value undergoing processing in this function
2470 * @return string The processed input value
2471 */
2472 public function stdWrap_encodeForJavaScriptValue($content = '')
2473 {
2474 return GeneralUtility::quoteJSvalue($content);
2475 }
2476
2477 /**
2478 * doubleBrTag
2479 * Searches for double line breaks and replaces them with the given value
2480 *
2481 * @param string $content Input value undergoing processing in this function.
2482 * @param array $conf stdWrap properties for doubleBrTag.
2483 * @return string The processed input value
2484 */
2485 public function stdWrap_doubleBrTag($content = '', $conf = [])
2486 {
2487 return preg_replace('/\R{1,2}[\t\x20]*\R{1,2}/', $conf['doubleBrTag'] ?? null, $content);
2488 }
2489
2490 /**
2491 * br
2492 * Searches for single line breaks and replaces them with a <br />/<br> tag
2493 * according to the doctype
2494 *
2495 * @param string $content Input value undergoing processing in this function.
2496 * @return string The processed input value
2497 */
2498 public function stdWrap_br($content = '')
2499 {
2500 return nl2br($content, !empty($this->getTypoScriptFrontendController()->xhtmlDoctype));
2501 }
2502
2503 /**
2504 * brTag
2505 * Searches for single line feeds and replaces them with the given value
2506 *
2507 * @param string $content Input value undergoing processing in this function.
2508 * @param array $conf stdWrap properties for brTag.
2509 * @return string The processed input value
2510 */
2511 public function stdWrap_brTag($content = '', $conf = [])
2512 {
2513 return str_replace(LF, $conf['brTag'] ?? null, $content);
2514 }
2515
2516 /**
2517 * encapsLines
2518 * Modifies text blocks by searching for lines which are not surrounded by HTML tags yet
2519 * and wrapping them with values given by stdWrap properties
2520 *
2521 * @param string $content Input value undergoing processing in this function.
2522 * @param array $conf stdWrap properties for erncapsLines.
2523 * @return string The processed input value
2524 */
2525 public function stdWrap_encapsLines($content = '', $conf = [])
2526 {
2527 return $this->encaps_lineSplit($content, $conf['encapsLines.']);
2528 }
2529
2530 /**
2531 * keywords
2532 * Transforms content into a CSV list to be used i.e. as keywords within a meta tag
2533 *
2534 * @param string $content Input value undergoing processing in this function.
2535 * @return string The processed input value
2536 */
2537 public function stdWrap_keywords($content = '')
2538 {
2539 return $this->keywords($content);
2540 }
2541
2542 /**
2543 * innerWrap
2544 * First of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2545 * See wrap
2546 *
2547 * @param string $content Input value undergoing processing in this function.
2548 * @param array $conf stdWrap properties for innerWrap.
2549 * @return string The processed input value
2550 */
2551 public function stdWrap_innerWrap($content = '', $conf = [])
2552 {
2553 return $this->wrap($content, $conf['innerWrap'] ?? null);
2554 }
2555
2556 /**
2557 * innerWrap2
2558 * Second of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2559 * See wrap
2560 *
2561 * @param string $content Input value undergoing processing in this function.
2562 * @param array $conf stdWrap properties for innerWrap2.
2563 * @return string The processed input value
2564 */
2565 public function stdWrap_innerWrap2($content = '', $conf = [])
2566 {
2567 return $this->wrap($content, $conf['innerWrap2'] ?? null);
2568 }
2569
2570 /**
2571 * addParams
2572 * Adds tag attributes to any content that is a tag
2573 *
2574 * @param string $content Input value undergoing processing in this function.
2575 * @param array $conf stdWrap properties for addParams.
2576 * @return string The processed input value
2577 * @deprecated since TYPO3 v9.5, will be removed in TYPO3 v10.0.
2578 */
2579 public function stdWrap_addParams($content = '', $conf = [])
2580 {
2581 return $this->addParams($content, $conf['addParams.'] ?? [], true);
2582 }
2583
2584 /**
2585 * filelink
2586 * Used to make lists of links to files
2587 * See wrap
2588 *
2589 * @param string $content Input value undergoing processing in this function.
2590 * @param array $conf stdWrap properties for filelink.
2591 * @return string The processed input value
2592 * @deprecated since TYPO3 v9.5, will be removed in TYPO3 v10.0. Use cObject FILES instead.
2593 */
2594 public function stdWrap_filelink($content = '', $conf = [])
2595 {
2596 return $this->filelink($content, $conf['filelink.'] ?? [], true);
2597 }
2598
2599 /**
2600 * preCObject
2601 * A content object that is prepended to the current content but between the innerWraps and the rest of the wraps
2602 *
2603 * @param string $content Input value undergoing processing in this function.
2604 * @param array $conf stdWrap properties for preCObject.
2605 * @return string The processed input value
2606 */
2607 public function stdWrap_preCObject($content = '', $conf = [])
2608 {
2609 return $this->cObjGetSingle($conf['preCObject'], $conf['preCObject.'], '/stdWrap/.preCObject') . $content;
2610 }
2611
2612 /**
2613 * postCObject
2614 * A content object that is appended to the current content but between the innerWraps and the rest of the wraps
2615 *
2616 * @param string $content Input value undergoing processing in this function.
2617 * @param array $conf stdWrap properties for postCObject.
2618 * @return string The processed input value
2619 */
2620 public function stdWrap_postCObject($content = '', $conf = [])
2621 {
2622 return $content . $this->cObjGetSingle($conf['postCObject'], $conf['postCObject.'], '/stdWrap/.postCObject');
2623 }
2624
2625 /**
2626 * wrapAlign
2627 * Wraps content with a div container having the style attribute text-align set to the given value
2628 * See wrap
2629 *
2630 * @param string $content Input value undergoing processing in this function.
2631 * @param array $conf stdWrap properties for wrapAlign.
2632 * @return string The processed input value
2633 */
2634 public function stdWrap_wrapAlign($content = '', $conf = [])
2635 {
2636 $wrapAlign = trim($conf['wrapAlign'] ?? '');
2637 if ($wrapAlign) {
2638 $content = $this->wrap($content, '<div style="text-align:' . htmlspecialchars($wrapAlign) . ';">|</div>');
2639 }
2640 return $content;
2641 }
2642
2643 /**
2644 * typolink
2645 * Wraps the content with a link tag
2646 * URLs and other attributes are created automatically by the values given in the stdWrap properties
2647 * See wrap
2648 *
2649 * @param string $content Input value undergoing processing in this function.
2650 * @param array $conf stdWrap properties for typolink.
2651 * @return string The processed input value
2652 */
2653 public function stdWrap_typolink($content = '', $conf = [])
2654 {
2655 return $this->typoLink($content, $conf['typolink.']);
2656 }
2657
2658 /**
2659 * wrap
2660 * This is the "mother" of all wraps
2661 * Third of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2662 * Basically it will put additional content before and after the current content using a split character as a placeholder for the current content
2663 * The default split character is | but it can be replaced with other characters by the property splitChar
2664 * Any other wrap that does not have own splitChar settings will be using the default split char though
2665 *
2666 * @param string $content Input value undergoing processing in this function.
2667 * @param array $conf stdWrap properties for wrap.
2668 * @return string The processed input value
2669 */
2670 public function stdWrap_wrap($content = '', $conf = [])
2671 {
2672 return $this->wrap(
2673 $content,
2674 $conf['wrap'] ?? null,
2675 $conf['wrap.']['splitChar'] ?? '|'
2676 );
2677 }
2678
2679 /**
2680 * noTrimWrap
2681 * Fourth of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2682 * The major difference to any other wrap is, that this one can make use of whitespace without trimming *
2683 *
2684 * @param string $content Input value undergoing processing in this function.
2685 * @param array $conf stdWrap properties for noTrimWrap.
2686 * @return string The processed input value
2687 */
2688 public function stdWrap_noTrimWrap($content = '', $conf = [])
2689 {
2690 $splitChar = isset($conf['noTrimWrap.']['splitChar.'])
2691 ? $this->stdWrap($conf['noTrimWrap.']['splitChar'] ?? '', $conf['noTrimWrap.']['splitChar.'])
2692 : $conf['noTrimWrap.']['splitChar'] ?? '';
2693 if ($splitChar === null || $splitChar === '') {
2694 $splitChar = '|';
2695 }
2696 $content = $this->noTrimWrap(
2697 $content,
2698 $conf['noTrimWrap'],
2699 $splitChar
2700 );
2701 return $content;
2702 }
2703
2704 /**
2705 * wrap2
2706 * Fifth of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2707 * The default split character is | but it can be replaced with other characters by the property splitChar
2708 *
2709 * @param string $content Input value undergoing processing in this function.
2710 * @param array $conf stdWrap properties for wrap2.
2711 * @return string The processed input value
2712 */
2713 public function stdWrap_wrap2($content = '', $conf = [])
2714 {
2715 return $this->wrap(
2716 $content,
2717 $conf['wrap2'] ?? null,
2718 $conf['wrap2.']['splitChar'] ?? '|'
2719 );
2720 }
2721
2722 /**
2723 * dataWrap
2724 * Sixth of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2725 * Can fetch additional content the same way data does (i.e. {field:whatever}) and apply it to the wrap before that is applied to the content
2726 *
2727 * @param string $content Input value undergoing processing in this function.
2728 * @param array $conf stdWrap properties for dataWrap.
2729 * @return string The processed input value
2730 */
2731 public function stdWrap_dataWrap($content = '', $conf = [])
2732 {
2733 return $this->dataWrap($content, $conf['dataWrap']);
2734 }
2735
2736 /**
2737 * prepend
2738 * A content object that will be prepended to the current content after most of the wraps have already been applied
2739 *
2740 * @param string $content Input value undergoing processing in this function.
2741 * @param array $conf stdWrap properties for prepend.
2742 * @return string The processed input value
2743 */
2744 public function stdWrap_prepend($content = '', $conf = [])
2745 {
2746 return $this->cObjGetSingle($conf['prepend'], $conf['prepend.'], '/stdWrap/.prepend') . $content;
2747 }
2748
2749 /**
2750 * append
2751 * A content object that will be appended to the current content after most of the wraps have already been applied
2752 *
2753 * @param string $content Input value undergoing processing in this function.
2754 * @param array $conf stdWrap properties for append.
2755 * @return string The processed input value
2756 */
2757 public function stdWrap_append($content = '', $conf = [])
2758 {
2759 return $content . $this->cObjGetSingle($conf['append'], $conf['append.'], '/stdWrap/.append');
2760 }
2761
2762 /**
2763 * wrap3
2764 * Seventh of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2765 * The default split character is | but it can be replaced with other characters by the property splitChar
2766 *
2767 * @param string $content Input value undergoing processing in this function.
2768 * @param array $conf stdWrap properties for wrap3.
2769 * @return string The processed input value
2770 */
2771 public function stdWrap_wrap3($content = '', $conf = [])
2772 {
2773 return $this->wrap(
2774 $content,
2775 $conf['wrap3'] ?? null,
2776 $conf['wrap3.']['splitChar'] ?? '|'
2777 );
2778 }
2779
2780 /**
2781 * orderedStdWrap
2782 * Calls stdWrap for each entry in the provided array
2783 *
2784 * @param string $content Input value undergoing processing in this function.
2785 * @param array $conf stdWrap properties for orderedStdWrap.
2786 * @return string The processed input value
2787 */
2788 public function stdWrap_orderedStdWrap($content = '', $conf = [])
2789 {
2790 $sortedKeysArray = ArrayUtility::filterAndSortByNumericKeys($conf['orderedStdWrap.'], true);
2791 foreach ($sortedKeysArray as $key) {
2792 $content = $this->stdWrap($content, $conf['orderedStdWrap.'][$key . '.'] ?? null);
2793 }
2794 return $content;
2795 }
2796
2797 /**
2798 * outerWrap
2799 * Eighth of a set of different wraps which will be applied in a certain order before or after other functions that modify the content
2800 *
2801 * @param string $content Input value undergoing processing in this function.
2802 * @param array $conf stdWrap properties for outerWrap.
2803 * @return string The processed input value
2804 */
2805 public function stdWrap_outerWrap($content = '', $conf = [])
2806 {
2807 return $this->wrap($content, $conf['outerWrap'] ?? null);
2808 }
2809
2810 /**
2811 * insertData
2812 * Can fetch additional content the same way data does and replaces any occurrence of {field:whatever} with this content
2813 *
2814 * @param string $content Input value undergoing processing in this function.
2815 * @return string The processed input value
2816 */
2817 public function stdWrap_insertData($content = '')
2818 {
2819 return $this->insertData($content);
2820 }
2821
2822 /**
2823 * postUserFunc
2824 * Will execute a user function after the content has been modified by any other stdWrap function
2825 *
2826 * @param string $content Input value undergoing processing in this function.
2827 * @param array $conf stdWrap properties for postUserFunc.
2828 * @return string The processed input value
2829 */
2830 public function stdWrap_postUserFunc($content = '', $conf = [])
2831 {
2832 return $this->callUserFunction($conf['postUserFunc'], $conf['postUserFunc.'], $content);
2833 }
2834
2835 /**
2836 * postUserFuncInt
2837 * Will execute a user function after the content has been created and each time it is fetched from Cache
2838 * The result of this function itself will not be cached
2839 *
2840 * @param string $content Input value undergoing processing in this function.
2841 * @param array $conf stdWrap properties for postUserFuncInt.
2842 * @return string The processed input value
2843 */
2844 public function stdWrap_postUserFuncInt($content = '', $conf = [])
2845 {
2846 $substKey = 'INT_SCRIPT.' . $this->getTypoScriptFrontendController()->uniqueHash();
2847 $this->getTypoScriptFrontendController()->config['INTincScript'][$substKey] = [
2848 'content' => $content,
2849 'postUserFunc' => $conf['postUserFuncInt'],
2850 'conf' => $conf['postUserFuncInt.'],
2851 'type' => 'POSTUSERFUNC',
2852 'cObj' => serialize($this)
2853 ];
2854 $content = '<!--' . $substKey . '-->';
2855 return $content;
2856 }
2857
2858 /**
2859 * prefixComment
2860 * Will add HTML comments to the content to make it easier to identify certain content elements within the HTML output later on
2861 *
2862 * @param string $content Input value undergoing processing in this function.
2863 * @param array $conf stdWrap properties for prefixComment.
2864 * @return string The processed input value
2865 */
2866 public function stdWrap_prefixComment($content = '', $conf = [])
2867 {
2868 if (
2869 (!isset($this->getTypoScriptFrontendController()->config['config']['disablePrefixComment']) || !$this->getTypoScriptFrontendController()->config['config']['disablePrefixComment'])
2870 && !empty($conf['prefixComment'])
2871 ) {
2872 $content = $this->prefixComment($conf['prefixComment'], [], $content);
2873 }
2874 return $content;
2875 }
2876
2877 /**
2878 * editIcons
2879 * Will render icons for frontend editing as long as there is a BE user logged in
2880 *
2881 * @param string $content Input value undergoing processing in this function.
2882 * @param array $conf stdWrap properties for editIcons.
2883 * @return string The processed input value
2884 */
2885 public function stdWrap_editIcons($content = '', $conf = [])
2886 {
2887 if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn() && $conf['editIcons']) {
2888 if (!isset($conf['editIcons.']) || !is_array($conf['editIcons.'])) {
2889 $conf['editIcons.'] = [];
2890 }
2891 $content = $this->editIcons($content, $conf['editIcons'], $conf['editIcons.']);
2892 }
2893 return $content;
2894 }
2895
2896 /**
2897 * editPanel
2898 * Will render the edit panel for frontend editing as long as there is a BE user logged in
2899 *
2900 * @param string $content Input value undergoing processing in this function.
2901 * @param array $conf stdWrap properties for editPanel.
2902 * @return string The processed input value
2903 */
2904 public function stdWrap_editPanel($content = '', $conf = [])
2905 {
2906 if ($this->getTypoScriptFrontendController()->isBackendUserLoggedIn()) {
2907 $content = $this->editPanel($content, $conf['editPanel.']);
2908 }
2909 return $content;
2910 }
2911
2912 /**
2913 * Store content into cache
2914 *
2915 * @param string $content Input value undergoing processing in these functions.
2916 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2917 * @return string The processed input value
2918 */
2919 public function stdWrap_cacheStore($content = '', $conf = [])
2920 {
2921 if (!isset($conf['cache.'])) {
2922 return $content;
2923 }
2924 $key = $this->calculateCacheKey($conf['cache.']);
2925 if (empty($key)) {
2926 return $content;
2927 }
2928 /** @var \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheFrontend */
2929 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
2930 $tags = $this->calculateCacheTags($conf['cache.']);
2931 $lifetime = $this->calculateCacheLifetime($conf['cache.']);
2932 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['stdWrap_cacheStore'] ?? [] as $_funcRef) {
2933 $params = [
2934 'key' => $key,
2935 'content' => $content,
2936 'lifetime' => $lifetime,
2937 'tags' => $tags
2938 ];
2939 GeneralUtility::callUserFunction($_funcRef, $params, $this);
2940 }
2941 $cacheFrontend->set($key, $content, $tags, $lifetime);
2942 return $content;
2943 }
2944
2945 /**
2946 * stdWrap post process hook
2947 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2948 * this hook executes functions at after the content has been modified by the rest of the stdWrap functions but still before debugging
2949 *
2950 * @param string $content Input value undergoing processing in these functions.
2951 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2952 * @return string The processed input value
2953 */
2954 public function stdWrap_st