[TASK] Use GeneralUtility::quoteJSvalue() where needed part 3
[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 $onClick = 'openPic('
1734 . GeneralUtility::quoteJSvalue($GLOBALS['TSFE']->baseUrlWrap($url)) . ','
1735 . '\'' . ($newWindow ? md5($url) : 'thePicture') . '\','
1736 . GeneralUtility::quoteJSvalue('width=' . ($processedFile->getProperty('width') + $offset[0])
1737 . ',height=' . ($processedFile->getProperty('height') + $offset[1]) . ',status=0,menubar=0')
1738 . '); return false;';
1739 $a1 = '<a href="' . htmlspecialchars($url) . '" onclick="' . htmlspecialchars($onClick) . '"' . $target . $GLOBALS['TSFE']->ATagParams . '>';
1740 $a2 = '</a>';
1741 $GLOBALS['TSFE']->setJS('openPic');
1742 } else {
1743 $conf['linkParams.']['parameter'] = $url;
1744 $string = $this->typoLink($string, $conf['linkParams.']);
1745 }
1746 if (isset($conf['stdWrap.'])) {
1747 $string = $this->stdWrap($string, $conf['stdWrap.']);
1748 }
1749 $content = $a1 . $string . $a2;
1750 }
1751 }
1752 return $content;
1753 }
1754
1755 /**
1756 * Returns content of a file. If it's an image the content of the file is not returned but rather an image tag is.
1757 *
1758 * @param string $fName The filename, being a TypoScript resource data type
1759 * @param string $addParams Additional parameters (attributes). Default is empty alt and title tags.
1760 * @return string If jpg,gif,jpeg,png: returns image_tag with picture in. If html,txt: returns content string
1761 * @see FILE()
1762 */
1763 public function fileResource($fName, $addParams = 'alt="" title=""') {
1764 $incFile = $GLOBALS['TSFE']->tmpl->getFileName($fName);
1765 if ($incFile && file_exists($incFile)) {
1766 $fileinfo = GeneralUtility::split_fileref($incFile);
1767 if (GeneralUtility::inList('jpg,gif,jpeg,png', $fileinfo['fileext'])) {
1768 $imgFile = $incFile;
1769 $imgInfo = @getImageSize($imgFile);
1770 return '<img src="' . $GLOBALS['TSFE']->absRefPrefix . $imgFile . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '"' . $this->getBorderAttr(' border="0"') . ' ' . $addParams . ' />';
1771 } elseif (filesize($incFile) < 1024 * 1024) {
1772 return $GLOBALS['TSFE']->tmpl->fileContent($incFile);
1773 }
1774 }
1775 }
1776
1777 /**
1778 * Sets the SYS_LASTCHANGED timestamp if input timestamp is larger than current value.
1779 * The SYS_LASTCHANGED timestamp can be used by various caching/indexing applications to determine if the page has new content.
1780 * Therefore you should call this function with the last-changed timestamp of any element you display.
1781 *
1782 * @param int $tstamp Unix timestamp (number of seconds since 1970)
1783 * @return void
1784 * @see TypoScriptFrontendController::setSysLastChanged()
1785 */
1786 public function lastChanged($tstamp) {
1787 $tstamp = (int)$tstamp;
1788 if ($tstamp > (int)$GLOBALS['TSFE']->register['SYS_LASTCHANGED']) {
1789 $GLOBALS['TSFE']->register['SYS_LASTCHANGED'] = $tstamp;
1790 }
1791 }
1792
1793 /**
1794 * Wraps the input string by the $wrap value and implements the "linkWrap" data type as well.
1795 * 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.
1796 *
1797 * @param string $content Input string
1798 * @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string
1799 * @return string Wrapped output string
1800 * @see wrap(), cImage(), FILE()
1801 */
1802 public function linkWrap($content, $wrap) {
1803 $wrapArr = explode('|', $wrap);
1804 if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
1805 if ($uid = $GLOBALS['TSFE']->tmpl->rootLine[$reg[1]]['uid']) {
1806 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
1807 }
1808 }
1809 return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
1810 }
1811
1812 /**
1813 * An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element.
1814 * From the $conf array it implements the properties "altText", "titleText" and "longdescURL"
1815 *
1816 * @param array $conf TypoScript configuration properties
1817 * @param bool $longDesc If set, the longdesc attribute will be generated - must only be used for img elements!
1818 * @return string Parameter string containing alt and title parameters (if any)
1819 * @see IMGTEXT(), FILE(), FORM(), cImage(), filelink()
1820 */
1821 public function getAltParam($conf, $longDesc = TRUE) {
1822 $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
1823 $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
1824 if (isset($conf['longdescURL.']) && $GLOBALS['TSFE']->config['config']['doctype'] != 'html5') {
1825 $longDesc = $this->typoLink_URL($conf['longdescURL.']);
1826 } else {
1827 $longDesc = trim($conf['longdescURL']);
1828 }
1829 // "alt":
1830 $altParam = ' alt="' . htmlspecialchars($altText) . '"';
1831 // "title":
1832 $emptyTitleHandling = 'useAlt';
1833 $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
1834 // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
1835 if ($titleText || $emptyTitleHandling == 'keepEmpty') {
1836 $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
1837 } elseif (!$titleText && $emptyTitleHandling == 'useAlt') {
1838 $altParam .= ' title="' . htmlspecialchars($altText) . '"';
1839 }
1840 // "longDesc" URL
1841 if ($longDesc) {
1842 $altParam .= ' longdesc="' . htmlspecialchars(strip_tags($longDesc)) . '"';
1843 }
1844 return $altParam;
1845 }
1846
1847 /**
1848 * Removes forbidden characters and spaces from name/id attributes in the form tag and formfields
1849 *
1850 * @param string $name Input string
1851 * @return string the cleaned string
1852 * @see FORM()
1853 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, the method is moved to FormContentObject
1854 */
1855 public function cleanFormName($name) {
1856 GeneralUtility::logDeprecatedFunction();
1857 // Turn data[x][y] into data:x:y:
1858 $name = preg_replace('/\\[|\\]\\[?/', ':', trim($name));
1859 // Remove illegal chars like _
1860 return preg_replace('#[^:a-zA-Z0-9]#', '', $name);
1861 }
1862
1863 /**
1864 * An abstraction method to add parameters to an A tag.
1865 * Uses the ATagParams property.
1866 *
1867 * @param array $conf TypoScript configuration properties
1868 * @param bool $addGlobal If set, will add the global config.ATagParams to the link
1869 * @return string String containing the parameters to the A tag (if non empty, with a leading space)
1870 * @see IMGTEXT(), filelink(), makelinks(), typolink()
1871 */
1872 public function getATagParams($conf, $addGlobal = 1) {
1873 $aTagParams = '';
1874 if ($conf['ATagParams.']) {
1875 $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
1876 } elseif ($conf['ATagParams']) {
1877 $aTagParams = ' ' . $conf['ATagParams'];
1878 }
1879 if ($addGlobal) {
1880 $aTagParams = ' ' . trim(($GLOBALS['TSFE']->ATagParams . $aTagParams));
1881 }
1882 // Extend params
1883 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'])) {
1884 $_params = array(
1885 'conf' => &$conf,
1886 'aTagParams' => &$aTagParams
1887 );
1888 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] as $objRef) {
1889 $processor =& GeneralUtility::getUserObj($objRef);
1890 $aTagParams = $processor->process($_params, $this);
1891 }
1892 }
1893
1894 $aTagParams = trim($aTagParams);
1895 if (!empty($aTagParams)) {
1896 $aTagParams = ' ' . $aTagParams;
1897 }
1898
1899 return $aTagParams;
1900 }
1901
1902 /**
1903 * All extension links should ask this function for additional properties to their tags.
1904 * Designed to add for instance an "onclick" property for site tracking systems.
1905 *
1906 * @param string $URL URL of the website
1907 * @param string $TYPE
1908 * @return string The additional tag properties
1909 */
1910 public function extLinkATagParams($URL, $TYPE) {
1911 $out = '';
1912 if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
1913 $extLinkATagParamsHandler = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
1914 if (method_exists($extLinkATagParamsHandler, 'main')) {
1915 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
1916 }
1917 }
1918 return trim($out) ? ' ' . trim($out) : '';
1919 }
1920
1921 /***********************************************
1922 *
1923 * HTML template processing functions
1924 *
1925 ***********************************************/
1926 /**
1927 * Returns a subpart from the input content stream.
1928 * A subpart is a part of the input stream which is encapsulated in a
1929 * string matching the input string, $marker. If this string is found
1930 * inside of HTML comment tags the start/end points of the content block
1931 * returned will be that right outside that comment block.
1932 * Example: The contennt string is
1933 * "Hello <!--###sub1### begin--> World. How are <!--###sub1### end--> you?"
1934 * If $marker is "###sub1###" then the content returned is
1935 * " World. How are ". The input content string could just as well have
1936 * been "Hello ###sub1### World. How are ###sub1### you?" and the result
1937 * would be the same
1938 * Wrapper for \TYPO3\CMS\Core\Html\HtmlParser::getSubpart which behaves identical
1939 *
1940 * @param string $content The content stream, typically HTML template content.
1941 * @param string $marker The marker string, typically on the form "###[the marker string]###
1942 * @return string The subpart found, if found.
1943 */
1944 public function getSubpart($content, $marker) {
1945 return HtmlParser::getSubpart($content, $marker);
1946 }
1947
1948 /**
1949 * Substitute subpart in input template stream.
1950 * This function substitutes a subpart in $content with the content of
1951 * $subpartContent.
1952 * Wrapper for \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart which behaves identical
1953 *
1954 * @param string $content The content stream, typically HTML template content.
1955 * @param string $marker The marker string, typically on the form "###[the marker string]###
1956 * @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.
1957 * @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!
1958 * @return string The processed HTML content string.
1959 */
1960 public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1) {
1961 return HtmlParser::substituteSubpart($content, $marker, $subpartContent, $recursive);
1962 }
1963
1964 /**
1965 * Substitues multiple subparts at once
1966 *
1967 * @param string $content The content stream, typically HTML template content.
1968 * @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.
1969 * @return string The processed HTML content string.
1970 */
1971 public function substituteSubpartArray($content, array $subpartsContent) {
1972 return HtmlParser::substituteSubpartArray($content, $subpartsContent);
1973 }
1974
1975 /**
1976 * Substitutes a marker string in the input content
1977 * (by a simple str_replace())
1978 *
1979 * @param string $content The content stream, typically HTML template content.
1980 * @param string $marker The marker string, typically on the form "###[the marker string]###
1981 * @param mixed $markContent The content to insert instead of the marker string found.
1982 * @return string The processed HTML content string.
1983 * @see substituteSubpart()
1984 */
1985 public function substituteMarker($content, $marker, $markContent) {
1986 return HtmlParser::substituteMarker($content, $marker, $markContent);
1987 }
1988
1989 /**
1990 * Multi substitution function with caching.
1991 *
1992 * This function should be a one-stop substitution function for working
1993 * with HTML-template. It does not substitute by str_replace but by
1994 * splitting. This secures that the value inserted does not themselves
1995 * contain markers or subparts.
1996 *
1997 * Note that the "caching" won't cache the content of the substition,
1998 * but only the splitting of the template in various parts. So if you
1999 * want only one cache-entry per template, make sure you always pass the
2000 * exact same set of marker/subpart keys. Else you will be flooding the
2001 * users cache table.
2002 *
2003 * This function takes three kinds of substitutions in one:
2004 * $markContentArray is a regular marker-array where the 'keys' are
2005 * substituted in $content with their values
2006 *
2007 * $subpartContentArray works exactly like markContentArray only is whole
2008 * subparts substituted and not only a single marker.
2009 *
2010 * $wrappedSubpartContentArray is an array of arrays with 0/1 keys where
2011 * the subparts pointed to by the main key is wrapped with the 0/1 value
2012 * alternating.
2013 *
2014 * @param string $content The content stream, typically HTML template content.
2015 * @param array $markContentArray Regular marker-array where the 'keys' are substituted in $content with their values
2016 * @param array $subpartContentArray Exactly like markContentArray only is whole subparts substituted and not only a single marker.
2017 * @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.
2018 * @return string The output content stream
2019 * @see substituteSubpart(), substituteMarker(), substituteMarkerInObject(), TEMPLATE()
2020 */
2021 public function substituteMarkerArrayCached($content, array $markContentArray = NULL, array $subpartContentArray = NULL, array $wrappedSubpartContentArray = NULL) {
2022 $GLOBALS['TT']->push('substituteMarkerArrayCached');
2023 // If not arrays then set them
2024 if (is_null($markContentArray)) {
2025 // Plain markers
2026 $markContentArray = array();
2027 }
2028 if (is_null($subpartContentArray)) {
2029 // Subparts being directly substituted
2030 $subpartContentArray = array();
2031 }
2032 if (is_null($wrappedSubpartContentArray)) {
2033 // Subparts being wrapped
2034 $wrappedSubpartContentArray = array();
2035 }
2036 // Finding keys and check hash:
2037 $sPkeys = array_keys($subpartContentArray);
2038 $wPkeys = array_keys($wrappedSubpartContentArray);
2039 $aKeys = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
2040 if (!count($aKeys)) {
2041 $GLOBALS['TT']->pull();
2042 return $content;
2043 }
2044 asort($aKeys);
2045 $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize(array(
2046 $content,
2047 $aKeys
2048 )));
2049 if ($this->substMarkerCache[$storeKey]) {
2050 $storeArr = $this->substMarkerCache[$storeKey];
2051 $GLOBALS['TT']->setTSlogMessage('Cached', 0);
2052 } else {
2053 $storeArrDat = $GLOBALS['TSFE']->sys_page->getHash($storeKey);
2054 if (is_array($storeArrDat)) {
2055 $storeArr = $storeArrDat;
2056 // Setting cache:
2057 $this->substMarkerCache[$storeKey] = $storeArr;
2058 $GLOBALS['TT']->setTSlogMessage('Cached from DB', 0);
2059 } else {
2060 // Initialize storeArr
2061 $storeArr = array();
2062 // Finding subparts and substituting them with the subpart as a marker
2063 foreach ($sPkeys as $sPK) {
2064 $content = $this->substituteSubpart($content, $sPK, $sPK);
2065 }
2066 // Finding subparts and wrapping them with markers
2067 foreach ($wPkeys as $wPK) {
2068 $content = $this->substituteSubpart($content, $wPK, array(
2069 $wPK,
2070 $wPK
2071 ));
2072 }
2073 // Traverse keys and quote them for reg ex.
2074 foreach ($aKeys as $tK => $tV) {
2075 $aKeys[$tK] = preg_quote($tV, '/');
2076 }
2077 $regex = '/' . implode('|', $aKeys) . '/';
2078 // Doing regex's
2079 $storeArr['c'] = preg_split($regex, $content);
2080 preg_match_all($regex, $content, $keyList);
2081 $storeArr['k'] = $keyList[0];
2082 // Setting cache:
2083 $this->substMarkerCache[$storeKey] = $storeArr;
2084 // Storing the cached data:
2085 $GLOBALS['TSFE']->sys_page->storeHash($storeKey, $storeArr, 'substMarkArrayCached');
2086 $GLOBALS['TT']->setTSlogMessage('Parsing', 0);
2087 }
2088 }
2089 // Substitution/Merging:
2090 // Merging content types together, resetting
2091 $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
2092 $wSCA_reg = array();
2093 $content = '';
2094 // Traversing the keyList array and merging the static and dynamic content
2095 foreach ($storeArr['k'] as $n => $keyN) {
2096 $content .= $storeArr['c'][$n];
2097 if (!is_array($valueArr[$keyN])) {
2098 $content .= $valueArr[$keyN];
2099 } else {
2100 $content .= $valueArr[$keyN][(int)$wSCA_reg[$keyN] % 2];
2101 $wSCA_reg[$keyN]++;
2102 }
2103 }
2104 $content .= $storeArr['c'][count($storeArr['k'])];
2105 $GLOBALS['TT']->pull();
2106 return $content;
2107 }
2108
2109 /**
2110 * Traverses the input $markContentArray array and for each key the marker
2111 * by the same name (possibly wrapped and in upper case) will be
2112 * substituted with the keys value in the array.
2113 *
2114 * This is very useful if you have a data-record to substitute in some
2115 * content. In particular when you use the $wrap and $uppercase values to
2116 * pre-process the markers. Eg. a key name like "myfield" could effectively
2117 * be represented by the marker "###MYFIELD###" if the wrap value
2118 * was "###|###" and the $uppercase boolean TRUE.
2119 *
2120 * @param string $content The content stream, typically HTML template content.
2121 * @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.
2122 * @param string $wrap A wrap value - [part 1] | [part 2] - for the markers before substitution
2123 * @param bool $uppercase If set, all marker string substitution is done with upper-case markers.
2124 * @param bool $deleteUnused If set, all unused marker are deleted.
2125 * @return string The processed output stream
2126 * @see substituteMarker(), substituteMarkerInObject(), TEMPLATE()
2127 */
2128 public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
2129 return HtmlParser::substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
2130 }
2131
2132 /**
2133 * Substitute marker array in an array of values
2134 *
2135 * @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)
2136 * @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.
2137 * @return mixed The processed input variable.
2138 * @see substituteMarker()
2139 */
2140 public function substituteMarkerInObject(&$tree, array $markContentArray) {
2141 if (is_array($tree)) {
2142 foreach ($tree as $key => $value) {
2143 $this->substituteMarkerInObject($tree[$key], $markContentArray);
2144 }
2145 } else {
2146 $tree = $this->substituteMarkerArray($tree, $markContentArray);
2147 }
2148 return $tree;
2149 }
2150
2151 /**
2152 * Replaces all markers and subparts in a template with the content provided in the structured array.
2153 *
2154 * @param string $content
2155 * @param array $markersAndSubparts
2156 * @param string $wrap
2157 * @param bool $uppercase
2158 * @param bool $deleteUnused
2159 * @return string
2160 */
2161 public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
2162 return HtmlParser::substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
2163 }
2164
2165 /**
2166 * Adds elements to the input $markContentArray based on the values from
2167 * the fields from $fieldList found in $row
2168 *
2169 * @param array $markContentArray Array with key/values being marker-strings/substitution values.
2170 * @param array $row An array with keys found in the $fieldList (typically a record) which values should be moved to the $markContentArray
2171 * @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)
2172 * @param bool $nl2br If set, all values added to $markContentArray will be nl2br()'ed
2173 * @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 "###
2174 * @param bool $HSC If set, all values are passed through htmlspecialchars() - RECOMMENDED to avoid most obvious XSS and maintain XHTML compliance.
2175 * @return array The modified $markContentArray
2176 */
2177 public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = TRUE, $prefix = 'FIELD_', $HSC = FALSE) {
2178 if ($fieldList) {
2179 $fArr = GeneralUtility::trimExplode(',', $fieldList, TRUE);
2180 foreach ($fArr as $field) {
2181 $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], !empty($GLOBALS['TSFE']->xhtmlDoctype)) : $row[$field];
2182 }
2183 } else {
2184 if (is_array($row)) {
2185 foreach ($row as $field => $value) {
2186 if (!MathUtility::canBeInterpretedAsInteger($field)) {
2187 if ($HSC) {
2188 $value = htmlspecialchars($value);
2189 }
2190 $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, !empty($GLOBALS['TSFE']->xhtmlDoctype)) : $value;
2191 }
2192 }
2193 }
2194 }
2195 return $markContentArray;
2196 }
2197
2198 /**
2199 * Sets the current file object during iterations over files.
2200 *
2201 * @param File $fileObject The file object.
2202 */
2203 public function setCurrentFile($fileObject) {
2204 $this->currentFile = $fileObject;
2205 }
2206
2207 /**
2208 * Gets the current file object during iterations over files.
2209 *
2210 * @return File The current file object.
2211 */
2212 public function getCurrentFile() {
2213 return $this->currentFile;
2214 }
2215
2216 /***********************************************
2217 *
2218 * "stdWrap" + sub functions
2219 *
2220 ***********************************************/
2221 /**
2222 * The "stdWrap" function. This is the implementation of what is known as "stdWrap properties" in TypoScript.
2223 * Basically "stdWrap" performs some processing of a value based on properties in the input $conf array(holding the TypoScript "stdWrap properties")
2224 * 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.
2225 *
2226 * If $this->alternativeData is an array it's used instead of the $this->data array in ->getData
2227 *
2228 * @param string $content Input value undergoing processing in this function. Possibly substituted by other values fetched from another source.
2229 * @param array $conf TypoScript "stdWrap properties".
2230 * @return string The processed input value
2231 */
2232 public function stdWrap($content = '', $conf = array()) {
2233 // If there is any hook object, activate all of the process and override functions.
2234 // The hook interface ContentObjectStdWrapHookInterface takes care that all 4 methods exist.
2235 if ($this->stdWrapHookObjects) {
2236 $conf['stdWrapPreProcess'] = 1;
2237 $conf['stdWrapOverride'] = 1;
2238 $conf['stdWrapProcess'] = 1;
2239 $conf['stdWrapPostProcess'] = 1;
2240 }
2241
2242 if (!is_array($conf) || !$conf) {
2243 return $content;
2244 }
2245
2246 // Cache handling
2247 if (is_array($conf['cache.'])) {
2248 $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
2249 $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
2250 $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
2251 $conf['cacheRead'] = 1;
2252 $conf['cacheStore'] = 1;
2253 }
2254 // Check, which of the available stdWrap functions is needed for the current conf Array
2255 // and keep only those but still in the same order
2256 $sortedConf = array_intersect_key($this->stdWrapOrder, $conf);
2257 // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
2258 $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
2259 // Additional Array to check whether a function has already been executed
2260 $isExecuted = array();
2261 // Additional switch to make sure 'required', 'if' and 'fieldRequired'
2262 // will still stop rendering immediately in case they return FALSE
2263 $this->stdWrapRecursionLevel++;
2264 $this->stopRendering[$this->stdWrapRecursionLevel] = FALSE;
2265 // execute each function in the predefined order
2266 foreach ($sortedConf as $stdWrapName => $functionType) {
2267 // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
2268 if (!$isExecuted[$stdWrapName] && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
2269 $functionName = rtrim($stdWrapName, '.');
2270 $functionProperties = $functionName . '.';
2271 // If there is any code on the next level, check if it contains "official" stdWrap functions
2272 // if yes, execute them first - will make each function stdWrap aware
2273 // so additional stdWrap calls within the functions can be removed, since the result will be the same
2274 // exception: the recursive stdWrap function and cObject will still be using their own stdWrap call, since it modifies the content and not a property
2275 if ($functionName !== 'stdWrap' && !empty($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
2276 if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
2277 $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
2278 }
2279 }
2280 // Check if key is still containing something, since it might have been changed by next level stdWrap before
2281 if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
2282 // Get just that part of $conf that is needed for the particular function
2283 $singleConf = array(
2284 $functionName => $conf[$functionName],
2285 $functionProperties => $conf[$functionProperties]
2286 );
2287 // In this special case 'spaceBefore' and 'spaceAfter' need additional stuff from 'space.''
2288 if ($functionName === 'spaceBefore' || $functionName === 'spaceAfter') {
2289 $singleConf['space.'] = $conf['space.'];
2290 }
2291 // Hand over the whole $conf array to the stdWrapHookObjects
2292 if ($functionType === 'hook') {
2293 $singleConf = $conf;
2294 }
2295 // Add both keys - with and without the dot - to the set of executed functions
2296 $isExecuted[$functionName] = TRUE;
2297 $isExecuted[$functionProperties] = TRUE;
2298 // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
2299 $functionName = 'stdWrap_' . $functionName;
2300 $content = $this->{$functionName}($content, $singleConf);
2301 } elseif ($functionType == 'boolean' && !$conf[$functionName]) {
2302 $isExecuted[$functionName] = TRUE;
2303 $isExecuted[$functionProperties] = TRUE;
2304 }
2305 }
2306 }
2307 unset($this->stopRendering[$this->stdWrapRecursionLevel]);
2308 $this->stdWrapRecursionLevel--;
2309
2310 return $content;
2311 }
2312
2313 /**
2314 * Gets a configuration value by passing them through stdWrap first and taking a default value if stdWrap doesn't yield a result.
2315 *
2316 * @param string $key The config variable key (from TS array).
2317 * @param array $config The TypoScript array.
2318 * @param string $defaultValue Optional default value.
2319 * @return string Value of the config variable
2320 */
2321 public function stdWrapValue($key, array $config, $defaultValue = '') {
2322 if (isset($config[$key])) {
2323 if (!isset($config[$key . '.'])) {
2324 return $config[$key];
2325 }
2326 } elseif (isset($config[$key . '.'])) {
2327 $config[$key] = '';
2328 } else {
2329 return $defaultValue;
2330 }
2331 $stdWrapped = $this->stdWrap($config[$key], $config[$key . '.']);
2332 return $stdWrapped ?: $defaultValue;
2333 }
2334
2335 /**
2336 * stdWrap pre process hook
2337 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2338 * this hook will execute functions before any other stdWrap function can modify anything
2339 *
2340 * @param string $content Input value undergoing processing in these functions.
2341 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2342 * @return string The processed input value
2343 */
2344 public function stdWrap_stdWrapPreProcess($content = '', $conf = array()) {
2345 foreach ($this->stdWrapHookObjects as $hookObject) {
2346 $content = $hookObject->stdWrapPreProcess($content, $conf, $this);
2347 }
2348 return $content;
2349 }
2350
2351 /**
2352 * Check if content was cached before (depending on the given cache key)
2353 *
2354 * @param string $content Input value undergoing processing in these functions.
2355 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2356 * @return string The processed input value
2357 */
2358 public function stdWrap_cacheRead($content = '', $conf = array()) {
2359 if (!empty($conf['cache.']['key'])) {
2360 /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */
2361 $cacheFrontend = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
2362 if ($cacheFrontend && $cacheFrontend->has($conf['cache.']['key'])) {
2363 $content = $cacheFrontend->get($conf['cache.']['key']);
2364 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2365 }
2366 }
2367 return $content;
2368 }
2369
2370 /**
2371 * Add tags to page cache (comma-separated list)
2372 *
2373 * @param string $content Input value undergoing processing in these functions.
2374 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2375 * @return string The processed input value
2376 */
2377 public function stdWrap_addPageCacheTags($content = '', $conf = array()) {
2378 $tags = isset($conf['addPageCacheTags.'])
2379 ? $this->stdWrap($conf['addPageCacheTags'], $conf['addPageCacheTags.'])
2380 : $conf['addPageCacheTags'];
2381 if (!empty($tags)) {
2382 $cacheTags = GeneralUtility::trimExplode(',', $tags, TRUE);
2383 $GLOBALS['TSFE']->addCacheTags($cacheTags);
2384 }
2385 return $content;
2386 }
2387
2388 /**
2389 * setContentToCurrent
2390 * actually it just does the contrary: Sets the value of 'current' based on current content
2391 *
2392 * @param string $content Input value undergoing processing in this function.
2393 * @param array $conf stdWrap properties for setContentToCurrent.
2394 * @return string The processed input value
2395 */
2396 public function stdWrap_setContentToCurrent($content = '', $conf = array()) {
2397 $this->data[$this->currentValKey] = $content;
2398 return $content;
2399 }
2400
2401 /**
2402 * setCurrent
2403 * Sets the value of 'current' based on the outcome of stdWrap operations
2404 *
2405 * @param string $content Input value undergoing processing in this function.
2406 * @param array $conf stdWrap properties for setCurrent.
2407 * @return string The processed input value
2408 */
2409 public function stdWrap_setCurrent($content = '', $conf = array()) {
2410 $this->data[$this->currentValKey] = $conf['setCurrent'];
2411 return $content;
2412 }
2413
2414 /**
2415 * lang
2416 * Translates content based on the language currently used by the FE
2417 *
2418 * @param string $content Input value undergoing processing in this function.
2419 * @param array $conf stdWrap properties for lang.
2420 * @return string The processed input value
2421 */
2422 public function stdWrap_lang($content = '', $conf = array()) {
2423 if (isset($conf['lang.']) && $GLOBALS['TSFE']->config['config']['language'] && isset($conf['lang.'][$GLOBALS['TSFE']->config['config']['language']])) {
2424 $content = $conf['lang.'][$GLOBALS['TSFE']->config['config']['language']];
2425 }
2426 return $content;
2427 }
2428
2429 /**
2430 * data
2431 * Gets content from different sources based on getText functions, makes use of alternativeData, when set
2432 *
2433 * @param string $content Input value undergoing processing in this function.
2434 * @param array $conf stdWrap properties for data.
2435 * @return string The processed input value
2436 */
2437 public function stdWrap_data($content = '', $conf = array()) {
2438 $content = $this->getData($conf['data'], is_array($this->alternativeData) ? $this->alternativeData : $this->data);
2439 // This must be unset directly after
2440 $this->alternativeData = '';
2441 return $content;
2442 }
2443
2444 /**
2445 * field
2446 * Gets content from a DB field
2447 *
2448 * @param string $content Input value undergoing processing in this function.
2449 * @param array $conf stdWrap properties for field.
2450 * @return string The processed input value
2451 */
2452 public function stdWrap_field($content = '', $conf = array()) {
2453 return $this->getFieldVal($conf['field']);
2454 }
2455
2456 /**
2457 * current
2458 * Gets content that has been perviously set as 'current'
2459 * Can be set via setContentToCurrent or setCurrent or will be set automatically i.e. inside the split function
2460 *
2461 * @param string $content Input value undergoing processing in this function.
2462 * @param array $conf stdWrap properties for current.
2463 * @return string The processed input value
2464 */
2465 public function stdWrap_current($content = '', $conf = array()) {
2466 return $this->data[$this->currentValKey];
2467 }
2468
2469 /**
2470 * cObject
2471 * Will replace the content with the value of a any official TypoScript cObject
2472 * like TEXT, COA, HMENU
2473 *
2474 * @param string $content Input value undergoing processing in this function.
2475 * @param array $conf stdWrap properties for cObject.
2476 * @return string The processed input value
2477 */
2478 public function stdWrap_cObject($content = '', $conf = array()) {
2479 return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
2480 }
2481
2482 /**
2483 * numRows
2484 * Counts the number of returned records of a DB operation
2485 * makes use of select internally
2486 *
2487 * @param string $content Input value undergoing processing in this function.
2488 * @param array $conf stdWrap properties for numRows.
2489 * @return string The processed input value
2490 */
2491 public function stdWrap_numRows($content = '', $conf = array()) {
2492 return $this->numRows($conf['numRows.']);
2493 }
2494
2495 /**
2496 * filelist
2497 * Will create a list of files based on some additional parameters
2498 *
2499 * @param string $content Input value undergoing processing in this function.
2500 * @param array $conf stdWrap properties for filelist.
2501 * @return string The processed input value
2502 */
2503 public function stdWrap_filelist($content = '', $conf = array()) {
2504 return $this->filelist($conf['filelist']);
2505 }
2506
2507 /**
2508 * preUserFunc
2509 * Will execute a user public function before the content will be modified by any other stdWrap function
2510 *
2511 * @param string $content Input value undergoing processing in this function.
2512 * @param array $conf stdWrap properties for preUserFunc.
2513 * @return string The processed input value
2514 */
2515 public function stdWrap_preUserFunc($content = '', $conf = array()) {
2516 return $this->callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'], $content);
2517 }
2518
2519 /**
2520 * stdWrap override hook
2521 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2522 * this hook will execute functions on existing content but still before the content gets modified or replaced
2523 *
2524 * @param string $content Input value undergoing processing in these functions.
2525 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2526 * @return string The processed input value
2527 */
2528 public function stdWrap_stdWrapOverride($content = '', $conf = array()) {
2529 foreach ($this->stdWrapHookObjects as $hookObject) {
2530 $content = $hookObject->stdWrapOverride($content, $conf, $this);
2531 }
2532 return $content;
2533 }
2534
2535 /**
2536 * override
2537 * Will override the current value of content with its own value'
2538 *
2539 * @param string $content Input value undergoing processing in this function.
2540 * @param array $conf stdWrap properties for override.
2541 * @return string The processed input value
2542 */
2543 public function stdWrap_override($content = '', $conf = array()) {
2544 if (trim($conf['override'])) {
2545 $content = $conf['override'];
2546 }
2547 return $content;
2548 }
2549
2550 /**
2551 * preIfEmptyListNum
2552 * Gets a value off a CSV list before the following ifEmpty check
2553 * 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
2554 *
2555 * @param string $content Input value undergoing processing in this function.
2556 * @param array $conf stdWrap properties for preIfEmptyListNum.
2557 * @return string The processed input value
2558 */
2559 public function stdWrap_preIfEmptyListNum($content = '', $conf = array()) {
2560 return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
2561 }
2562
2563 /**
2564 * ifNull
2565 * Will set content to a replacement value in case the value of content is NULL
2566 *
2567 * @param string|NULL $content Input value undergoing processing in this function.
2568 * @param array $conf stdWrap properties for ifNull.
2569 * @return string The processed input value
2570 */
2571 public function stdWrap_ifNull($content = '', $conf = array()) {
2572 return $content !== NULL ? $content : $conf['ifNull'];
2573 }
2574
2575 /**
2576 * ifEmpty
2577 * Will set content to a replacement value in case the trimmed value of content returns FALSE
2578 * 0 (zero) will be replaced as well
2579 *
2580 * @param string $content Input value undergoing processing in this function.
2581 * @param array $conf stdWrap properties for ifEmpty.
2582 * @return string The processed input value
2583 */
2584 public function stdWrap_ifEmpty($content = '', $conf = array()) {
2585 if (!trim($content)) {
2586 $content = $conf['ifEmpty'];
2587 }
2588 return $content;
2589 }
2590
2591 /**
2592 * ifBlank
2593 * Will set content to a replacement value in case the trimmed value of content has no length
2594 * 0 (zero) will not be replaced
2595 *
2596 * @param string $content Input value undergoing processing in this function.
2597 * @param array $conf stdWrap properties for ifBlank.
2598 * @return string The processed input value
2599 */
2600 public function stdWrap_ifBlank($content = '', $conf = array()) {
2601 if (trim($content) === '') {
2602 $content = $conf['ifBlank'];
2603 }
2604 return $content;
2605 }
2606
2607 /**
2608 * listNum
2609 * Gets a value off a CSV list after ifEmpty check
2610 * Might return an empty value in case the CSV does not contain a value at the position given by listNum
2611 * Use preIfEmptyListNum to avoid that behaviour
2612 *
2613 * @param string $content Input value undergoing processing in this function.
2614 * @param array $conf stdWrap properties for listNum.
2615 * @return string The processed input value
2616 */
2617 public function stdWrap_listNum($content = '', $conf = array()) {
2618 return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
2619 }
2620
2621 /**
2622 * trim
2623 * Cuts off any whitespace at the beginning and the end of the content
2624 *
2625 * @param string $content Input value undergoing processing in this function.
2626 * @param array $conf stdWrap properties for trim.
2627 * @return string The processed input value
2628 */
2629 public function stdWrap_trim($content = '', $conf = array()) {
2630 return trim($content);
2631 }
2632
2633 /**
2634 * strPad
2635 * Will return a string padded left/right/on both sides, based on configuration given as stdWrap properties
2636 *
2637 * @param string $content Input value undergoing processing in this function.
2638 * @param array $conf stdWrap properties for strPad.
2639 * @return string The processed input value
2640 */
2641 public function stdWrap_strPad($content = '', $conf = array()) {
2642 // Must specify a length in conf for this to make sense
2643 $length = 0;
2644 // Padding with space is PHP-default
2645 $padWith = ' ';
2646 // Padding on the right side is PHP-default
2647 $padType = STR_PAD_RIGHT;
2648 if (!empty($conf['strPad.']['length'])) {
2649 $length = isset($conf['strPad.']['length.']) ? $this->stdWrap($conf['strPad.']['length'], $conf['strPad.']['length.']) : $conf['strPad.']['length'];
2650 $length = (int)$length;
2651 }
2652 if (isset($conf['strPad.']['padWith']) && (string)$conf['strPad.']['padWith'] !== '') {
2653 $padWith = isset($conf['strPad.']['padWith.']) ? $this->stdWrap($conf['strPad.']['padWith'], $conf['strPad.']['padWith.']) : $conf['strPad.']['padWith'];
2654 }
2655 if (!empty($conf['strPad.']['type'])) {
2656 $type = isset($conf['strPad.']['type.']) ? $this->stdWrap($conf['strPad.']['type'], $conf['strPad.']['type.']) : $conf['strPad.']['type'];
2657 if (strtolower($type) === 'left') {
2658 $padType = STR_PAD_LEFT;
2659 } elseif (strtolower($type) === 'both') {
2660 $padType = STR_PAD_BOTH;
2661 }
2662 }
2663 $content = str_pad($content, $length, $padWith, $padType);
2664 return $content;
2665 }
2666
2667 /**
2668 * stdWrap
2669 * A recursive call of the stdWrap function set
2670 * This enables the user to execute stdWrap functions in another than the predefined order
2671 * It modifies the content, not the property
2672 * while the new feature of chained stdWrap functions modifies the property and not the content
2673 *
2674 * @param string $content Input value undergoing processing in this function.
2675 * @param array $conf stdWrap properties for stdWrap.
2676 * @return string The processed input value
2677 */
2678 public function stdWrap_stdWrap($content = '', $conf = array()) {
2679 return $this->stdWrap($content, $conf['stdWrap.']);
2680 }
2681
2682 /**
2683 * stdWrap process hook
2684 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2685 * this hook executes functions directly after the recursive stdWrap function call but still before the content gets modified
2686 *
2687 * @param string $content Input value undergoing processing in these functions.
2688 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2689 * @return string The processed input value
2690 */
2691 public function stdWrap_stdWrapProcess($content = '', $conf = array()) {
2692 foreach ($this->stdWrapHookObjects as $hookObject) {
2693 $content = $hookObject->stdWrapProcess($content, $conf, $this);
2694 }
2695 return $content;
2696 }
2697
2698 /**
2699 * required
2700 * Will immediately stop rendering and return an empty value
2701 * when there is no content at this point
2702 *
2703 * @param string $content Input value undergoing processing in this function.
2704 * @param array $conf stdWrap properties for required.
2705 * @return string The processed input value
2706 */
2707 public function stdWrap_required($content = '', $conf = array()) {
2708 if ((string)$content === '') {
2709 $content = '';
2710 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2711 }
2712 return $content;
2713 }
2714
2715 /**
2716 * if
2717 * Will immediately stop rendering and return an empty value
2718 * when the result of the checks returns FALSE
2719 *
2720 * @param string $content Input value undergoing processing in this function.
2721 * @param array $conf stdWrap properties for if.
2722 * @return string The processed input value
2723 */
2724 public function stdWrap_if($content = '', $conf = array()) {
2725 if (empty($conf['if.']) || $this->checkIf($conf['if.'])) {
2726 return $content;
2727 }
2728 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2729 return '';
2730 }
2731
2732 /**
2733 * fieldRequired
2734 * Will immediately stop rendering and return an empty value
2735 * when there is no content in the field given by fieldRequired
2736 *
2737 * @param string $content Input value undergoing processing in this function.
2738 * @param array $conf stdWrap properties for fieldRequired.
2739 * @return string The processed input value
2740 */
2741 public function stdWrap_fieldRequired($content = '', $conf = array()) {
2742 if (!trim($this->data[$conf['fieldRequired']])) {
2743 $content = '';
2744 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2745 }
2746 return $content;
2747 }
2748
2749 /**
2750 * csConv
2751 * Will convert the current chracter set of the content to the one given in csConv
2752 *
2753 * @param string $content Input value undergoing processing in this function.
2754 * @param array $conf stdWrap properties for csConv.
2755 * @return string The processed input value
2756 */
2757 public function stdWrap_csConv($content = '', $conf = array()) {
2758 return $GLOBALS['TSFE']->csConv($content, $conf['csConv']);
2759 }
2760
2761 /**
2762 * parseFunc
2763 * Will parse the content based on functions given as stdWrap properties
2764 * Heavily used together with RTE based content
2765 *
2766 * @param string $content Input value undergoing processing in this function.
2767 * @param array $conf stdWrap properties for parseFunc.
2768 * @return string The processed input value
2769 */
2770 public function stdWrap_parseFunc($content = '', $conf = array()) {
2771 return $this->parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
2772 }
2773
2774 /**
2775 * HTMLparser
2776 * Will parse HTML content based on functions given as stdWrap properties
2777 * Heavily used together with RTE based content
2778 *
2779 * @param string $content Input value undergoing processing in this function.
2780 * @param array $conf stdWrap properties for HTMLparser.
2781 * @return string The processed input value
2782 */
2783 public function stdWrap_HTMLparser($content = '', $conf = array()) {
2784 if (is_array($conf['HTMLparser.'])) {
2785 $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
2786 }
2787 return $content;
2788 }
2789
2790 /**
2791 * split
2792 * Will split the content by a given token and treat the results separately
2793 * Automatically fills 'current' with a single result
2794 *
2795 * @param string $content Input value undergoing processing in this function.
2796 * @param array $conf stdWrap properties for split.
2797 * @return string The processed input value
2798 */
2799 public function stdWrap_split($content = '', $conf = array()) {
2800 return $this->splitObj($content, $conf['split.']);
2801 }
2802
2803 /**
2804 * replacement
2805 * Will execute replacements on the content (optionally with preg-regex)
2806 *
2807 * @param string $content Input value undergoing processing in this function.
2808 * @param array $conf stdWrap properties for replacement.
2809 * @return string The processed input value
2810 */
2811 public function stdWrap_replacement($content = '', $conf = array()) {
2812 return $this->replacement($content, $conf['replacement.']);
2813 }
2814
2815 /**
2816 * prioriCalc
2817 * Will use the content as a mathematical term and calculate the result
2818 * Can be set to 1 to just get a calculated value or 'intval' to get the integer of the result
2819 *
2820 * @param string $content Input value undergoing processing in this function.
2821 * @param array $conf stdWrap properties for prioriCalc.
2822 * @return string The processed input value
2823 */
2824 public function stdWrap_prioriCalc($content = '', $conf = array()) {
2825 $content = MathUtility::calculateWithParentheses($content);
2826 if ($conf['prioriCalc'] == 'intval') {
2827 $content = (int)$content;
2828 }
2829 return $content;
2830 }
2831
2832 /**
2833 * char
2834 * Will return a character based on its position within the current character set
2835 *
2836 * @param string $content Input value undergoing processing in this function.
2837 * @param array $conf stdWrap properties for char.
2838 * @return string The processed input value
2839 */
2840 public function stdWrap_char($content = '', $conf = array()) {
2841 return chr((int)$conf['char']);
2842 }
2843