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