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