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