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