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