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