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