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