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