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