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