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