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