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