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