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