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