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