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