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