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