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