[BUGFIX] Consider subfolder in absolute links
[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 $confOverride = is_array($conf) ? $conf : array();
729 list($name, $conf) = $cF->getVal($key, $GLOBALS['TSFE']->tmpl->setup);
730 $conf = array_replace_recursive(is_array($conf) ? $conf : array(), $confOverride);
731 // Getting the cObject
732 $GLOBALS['TT']->incStackPointer();
733 $content .= $this->cObjGetSingle($name, $conf, $key);
734 $GLOBALS['TT']->decStackPointer();
735 } else {
736 $hooked = FALSE;
737 // Application defined cObjects
738 if (!empty($this->cObjHookObjectsRegistry[$name])) {
739 if (empty($this->cObjHookObjectsArr[$name])) {
740 $this->cObjHookObjectsArr[$name] = GeneralUtility::getUserObj($this->cObjHookObjectsRegistry[$name]);
741 }
742 $hookObj = $this->cObjHookObjectsArr[$name];
743 if (method_exists($hookObj, 'cObjGetSingleExt')) {
744 $content .= $hookObj->cObjGetSingleExt($name, $conf, $TSkey, $this);
745 $hooked = TRUE;
746 }
747 }
748 if (!$hooked) {
749 $contentObject = $this->getContentObject($name);
750 if ($contentObject) {
751 $content .= $contentObject->render($conf);
752 } else {
753 // Call hook functions for extra processing
754 if ($name && is_array($TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'])) {
755 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_content.php']['cObjTypeAndClassDefault'] as $classData) {
756 $hookObject = GeneralUtility::getUserObj($classData);
757 if (!$hookObject instanceof \TYPO3\CMS\Frontend\ContentObject\ContentObjectGetSingleHookInterface) {
758 throw new \UnexpectedValueException('$hookObject must implement interface TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectGetSingleHookInterface', 1195043731);
759 }
760 /** @var $hookObject \TYPO3\CMS\Frontend\ContentObject\ContentObjectGetSingleHookInterface */
761 $content .= $hookObject->getSingleContentObject($name, (array) $conf, $TSkey, $this);
762 }
763 } else {
764 // Log error in AdminPanel
765 $warning = sprintf('Content Object "%s" does not exist', $name);
766 $GLOBALS['TT']->setTSlogMessage($warning, 2);
767 }
768 }
769 }
770 }
771 if ($GLOBALS['TT']->LR) {
772 $GLOBALS['TT']->pull($content);
773 }
774 }
775 // Increasing on exit...
776 $GLOBALS['TSFE']->cObjectDepthCounter++;
777 return $content;
778 }
779
780 /**
781 * Returns a new content object of type $name.
782 *
783 * @param string $name
784 * @return NULL|AbstractContentObject
785 */
786 public function getContentObject($name) {
787 if (!isset($this->contentObjectClassMapping[$name])) {
788 return NULL;
789 }
790 $fullyQualifiedClassName = 'TYPO3\\CMS\\Frontend\\ContentObject\\' . $this->contentObjectClassMapping[$name] . 'ContentObject';
791 return GeneralUtility::makeInstance($fullyQualifiedClassName, $this);
792 }
793
794 /********************************************
795 *
796 * Functions rendering content objects (cObjects)
797 *
798 ********************************************/
799
800 /**
801 * Rendering the cObject, FLOWPLAYER
802 *
803 * @param array $conf Array of TypoScript properties
804 * @return string Output
805 * @todo Define visibility
806 */
807 public function FLOWPLAYER($conf) {
808 return $this->getContentObject('FLOWPLAYER')->render($conf);
809 }
810
811 /**
812 * Rendering the cObject, TEXT
813 *
814 * @param array $conf Array of TypoScript properties
815 * @return string Output
816 * @todo Define visibility
817 */
818 public function TEXT($conf) {
819 return $this->getContentObject('TEXT')->render($conf);
820 }
821
822 /**
823 * Rendering the cObject, CLEARGIF
824 *
825 * @param array $conf Array of TypoScript properties
826 * @return string Output
827 * @todo Define visibility
828 */
829 public function CLEARGIF($conf) {
830 return $this->getContentObject('CLEARGIF')->render($conf);
831 }
832
833 /**
834 * Rendering the cObject, COBJ_ARRAY / COA and COBJ_ARRAY_INT
835 *
836 * @param array $conf Array of TypoScript properties
837 * @param string $ext If "INT" then the cObject is a "COBJ_ARRAY_INT" (non-cached), otherwise just "COBJ_ARRAY" (cached)
838 * @return string Output
839 * @todo Define visibility
840 */
841 public function COBJ_ARRAY($conf, $ext = '') {
842 if ($ext === 'INT') {
843 return $this->getContentObject('COA_INT')->render($conf);
844 } else {
845 return $this->getContentObject('COA')->render($conf);
846 }
847 }
848
849 /**
850 * Rendering the cObject, USER and USER_INT
851 *
852 * @param array $conf Array of TypoScript properties
853 * @param string $ext If "INT" then the cObject is a "USER_INT" (non-cached), otherwise just "USER" (cached)
854 * @return string Output
855 * @todo Define visibility
856 */
857 public function USER($conf, $ext = '') {
858 if ($ext === 'INT') {
859 return $this->getContentObject('USER_INT')->render($conf);
860 } else {
861 return $this->getContentObject('USER')->render($conf);
862 }
863 }
864
865 /**
866 * Retrieves a type of object called as USER or USER_INT. Object can detect their
867 * type by using this call. It returns OBJECTTYPE_USER_INT or OBJECTTYPE_USER depending on the
868 * current object execution. In all other cases it will return FALSE to indicate
869 * a call out of context.
870 *
871 * @return mixed One of OBJECTTYPE_ class constants or FALSE
872 */
873 public function getUserObjectType() {
874 return $this->userObjectType;
875 }
876
877 /**
878 * Sets the user object type
879 *
880 * @param mixed $userObjectType
881 * @return void
882 */
883 public function setUserObjectType($userObjectType) {
884 $this->userObjectType = $userObjectType;
885 }
886
887 /**
888 * Requests the current USER object to be converted to USER_INT.
889 *
890 * @return void
891 */
892 public function convertToUserIntObject() {
893 if ($this->userObjectType !== self::OBJECTTYPE_USER) {
894 $GLOBALS['TT']->setTSlogMessage('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer::convertToUserIntObject() ' . 'is called in the wrong context or for the wrong object type', 2);
895 } else {
896 $this->doConvertToUserIntObject = TRUE;
897 }
898 }
899
900 /**
901 * Rendering the cObject, FILE
902 *
903 * @param array $conf Array of TypoScript properties
904 * @return string Output
905 * @todo Define visibility
906 */
907 public function FILE($conf) {
908 return $this->getContentObject('FILE')->render($conf);
909 }
910
911 /**
912 * Rendering the cObject, FILES
913 *
914 * @param array $conf Array of TypoScript properties
915 * @return string Output
916 * @todo Define visibility
917 */
918 public function FILES($conf) {
919 return $this->getContentObject('FILES')->render($conf);
920 }
921
922 /**
923 * Rendering the cObject, IMAGE
924 *
925 * @param array $conf Array of TypoScript properties
926 * @return string Output
927 * @see cImage()
928 * @todo Define visibility
929 */
930 public function IMAGE($conf) {
931 return $this->getContentObject('IMAGE')->render($conf);
932 }
933
934 /**
935 * Rendering the cObject, IMG_RESOURCE
936 *
937 * @param array $conf Array of TypoScript properties
938 * @return string Output
939 * @see getImgResource()
940 * @todo Define visibility
941 */
942 public function IMG_RESOURCE($conf) {
943 return $this->getContentObject('IMG_RESOURCE')->render($conf);
944 }
945
946 /**
947 * Rendering the cObject, IMGTEXT
948 *
949 * @param array $conf Array of TypoScript properties
950 * @return string Output
951 * @todo Define visibility
952 */
953 public function IMGTEXT($conf) {
954 return $this->getContentObject('IMGTEXT')->render($conf);
955 }
956
957 /**
958 * Rendering the cObject, CONTENT
959 *
960 * @param array $conf Array of TypoScript properties
961 * @return string Output
962 * @todo Define visibility
963 */
964 public function CONTENT($conf) {
965 return $this->getContentObject('CONTENT')->render($conf);
966 }
967
968 /**
969 * Rendering the cObject, RECORDS
970 *
971 * @param array $conf Array of TypoScript properties
972 * @return string Output
973 * @todo Define visibility
974 */
975 public function RECORDS($conf) {
976 return $this->getContentObject('RECORDS')->render($conf);
977 }
978
979 /**
980 * Rendering the cObject, HMENU
981 *
982 * @param array $conf Array of TypoScript properties
983 * @return string Output
984 * @todo Define visibility
985 */
986 public function HMENU($conf) {
987 return $this->getContentObject('HMENU')->render($conf);
988 }
989
990 /**
991 * Rendering the cObject, CTABLE
992 *
993 * @param array $conf Array of TypoScript properties
994 * @return string Output
995 * @todo Define visibility
996 */
997 public function CTABLE($conf) {
998 return $this->getContentObject('CTABLE')->render($conf);
999 }
1000
1001 /**
1002 * Rendering the cObject, OTABLE
1003 *
1004 * @param array $conf Array of TypoScript properties
1005 * @return string Output
1006 * @todo Define visibility
1007 */
1008 public function OTABLE($conf) {
1009 return $this->getContentObject('OTABLE')->render($conf);
1010 }
1011
1012 /**
1013 * Rendering the cObject, COLUMNS
1014 *
1015 * @param array $conf Array of TypoScript properties
1016 * @return string Output
1017 * @todo Define visibility
1018 */
1019 public function COLUMNS($conf) {
1020 return $this->getContentObject('COLUMNS')->render($conf);
1021 }
1022
1023 /**
1024 * Rendering the cObject, HRULER
1025 *
1026 * @param array $conf Array of TypoScript properties
1027 * @return string Output
1028 * @todo Define visibility
1029 */
1030 public function HRULER($conf) {
1031 return $this->getContentObject('HRULER')->render($conf);
1032 }
1033
1034 /**
1035 * Rendering the cObject, CASE
1036 *
1037 * @param array $conf Array of TypoScript properties
1038 * @return string Output
1039 * @todo Define visibility
1040 */
1041 public function CASEFUNC($conf) {
1042 return $this->getContentObject('CASE')->render($conf);
1043 }
1044
1045 /**
1046 * Rendering the cObject, LOAD_REGISTER and RESTORE_REGISTER
1047 * NOTICE: This cObject does NOT return any content since it just sets internal data based on the TypoScript properties.
1048 *
1049 * @param array $conf Array of TypoScript properties
1050 * @param string $name If "RESTORE_REGISTER" then the cObject rendered is "RESTORE_REGISTER", otherwise "LOAD_REGISTER
1051 * @return string Empty string (the cObject only sets internal data!)
1052 * @todo Define visibility
1053 */
1054 public function LOAD_REGISTER($conf, $name) {
1055 if ($name === 'RESTORE_REGISTER') {
1056 return $this->getContentObject('RESTORE_REGISTER')->render();
1057 } else {
1058 return $this->getContentObject('LOAD_REGISTER')->render($conf);
1059 }
1060 }
1061
1062 /**
1063 * Rendering the cObject, FORM
1064 *
1065 * @param array $conf Array of TypoScript properties
1066 * @param array $formData Alternative formdata overriding whatever comes from TypoScript
1067 * @return string Output
1068 * @todo Define visibility
1069 */
1070 public function FORM($conf, $formData = '') {
1071 return $this->getContentObject('FORM')->render($conf, $formData);
1072 }
1073
1074 /**
1075 * Rendering the cObject, SEARCHRESULT
1076 *
1077 * @param array $conf Array of TypoScript properties
1078 * @return string Output
1079 * @todo Define visibility
1080 */
1081 public function SEARCHRESULT($conf) {
1082 return $this->getContentObject('SEARCHRESULT')->render($conf);
1083 }
1084
1085 /**
1086 * Rendering the cObject, TEMPLATE
1087 *
1088 * @param array $conf Array of TypoScript properties
1089 * @return string Output
1090 * @see substituteMarkerArrayCached()
1091 * @todo Define visibility
1092 */
1093 public function TEMPLATE($conf) {
1094 return $this->getContentObject('TEMPLATE')->render($conf);
1095 }
1096
1097 /**
1098 * Rendering the cObject, FLUIDTEMPLATE
1099 *
1100 * @param array $conf Array of TypoScript properties
1101 * @return string the HTML output
1102 * @author Steffen Ritter <info@steffen-ritter.net>
1103 * @author Benjamin Mack <benni@typo3.org>
1104 */
1105 protected function FLUIDTEMPLATE(array $conf) {
1106 return $this->getContentObject('FLUIDTEMPLATE')->render($conf);
1107 }
1108
1109 /**
1110 * Rendering the cObject, MULTIMEDIA
1111 *
1112 * @param array $conf Array of TypoScript properties
1113 * @return string Output
1114 * @todo Define visibility
1115 */
1116 public function MULTIMEDIA($conf) {
1117 return $this->getContentObject('MULTIMEDIA')->render($conf);
1118 }
1119
1120 /**
1121 * Rendering the cObject, MEDIA
1122 *
1123 * @param array $conf Array of TypoScript properties
1124 * @return string Output
1125 */
1126 public function MEDIA($conf) {
1127 return $this->getContentObject('MEDIA')->render($conf);
1128 }
1129
1130 /**
1131 * Rendering the cObject, SWFOBJECT
1132 *
1133 * @param array $conf Array of TypoScript properties
1134 * @return string Output
1135 */
1136 public function SWFOBJECT($conf) {
1137 return $this->getContentObject('SWFOBJECT')->render($conf);
1138 }
1139
1140 /**
1141 * Rendering the cObject, QTOBJECT
1142 *
1143 * @param array $conf Array of TypoScript properties
1144 * @return string Output
1145 */
1146 public function QTOBJECT($conf) {
1147 return $this->getContentObject('QTOBJECT')->render($conf);
1148 }
1149
1150 /**
1151 * Rendering the cObject, SVG
1152 *
1153 * @param array $conf Array of TypoScript properties
1154 * @return string Output
1155 */
1156 public function SVG($conf) {
1157 return $this->getContentObject('SVG')->render($conf);
1158 }
1159
1160 /************************************
1161 *
1162 * Various helper functions for content objects:
1163 *
1164 ************************************/
1165 /**
1166 * Converts a given config in Flexform to a conf-array
1167 *
1168 * @param string|array $flexData Flexform data
1169 * @param array $conf Array to write the data into, by reference
1170 * @param boolean $recursive Is set if called recursive. Don't call function with this parameter, it's used inside the function only
1171 * @return void
1172 */
1173 public function readFlexformIntoConf($flexData, &$conf, $recursive = FALSE) {
1174 if ($recursive === FALSE && is_string($flexData)) {
1175 $flexData = GeneralUtility::xml2array($flexData, 'T3');
1176 }
1177 if (is_array($flexData) && isset($flexData['data']['sDEF']['lDEF'])) {
1178 $flexData = $flexData['data']['sDEF']['lDEF'];
1179 }
1180 if (!is_array($flexData)) {
1181 return;
1182 }
1183 foreach ($flexData as $key => $value) {
1184 if (!is_array($value)) {
1185 continue;
1186 }
1187 if (isset($value['el'])) {
1188 if (is_array($value['el']) && count($value['el']) > 0) {
1189 foreach ($value['el'] as $ekey => $element) {
1190 if (isset($element['vDEF'])) {
1191 $conf[$ekey] = $element['vDEF'];
1192 } else {
1193 if (is_array($element)) {
1194 $this->readFlexformIntoConf($element, $conf[$key][key($element)][$ekey], TRUE);
1195 } else {
1196 $this->readFlexformIntoConf($element, $conf[$key][$ekey], TRUE);
1197 }
1198 }
1199 }
1200 } else {
1201 $this->readFlexformIntoConf($value['el'], $conf[$key], TRUE);
1202 }
1203 }
1204 if (isset($value['vDEF'])) {
1205 $conf[$key] = $value['vDEF'];
1206 }
1207 }
1208 }
1209
1210 /**
1211 * Returns all parents of the given PID (Page UID) list
1212 *
1213 * @param string $pidList A list of page Content-Element PIDs (Page UIDs) / stdWrap
1214 * @param array $pidConf stdWrap array for the list
1215 * @return string A list of PIDs
1216 * @access private
1217 * @todo Define visibility
1218 */
1219 public function getSlidePids($pidList, $pidConf) {
1220 $pidList = isset($pidConf) ? trim($this->stdWrap($pidList, $pidConf)) : trim($pidList);
1221 if ($pidList === '') {
1222 $pidList = 'this';
1223 }
1224 if (trim($pidList)) {
1225 $listArr = GeneralUtility::intExplode(',', str_replace('this', $GLOBALS['TSFE']->contentPid, $pidList));
1226 $listArr = $this->checkPidArray($listArr);
1227 }
1228 $pidList = array();
1229 if (is_array($listArr) && count($listArr)) {
1230 foreach ($listArr as $uid) {
1231 $page = $GLOBALS['TSFE']->sys_page->getPage($uid);
1232 if (!$page['is_siteroot']) {
1233 $pidList[] = $page['pid'];
1234 }
1235 }
1236 }
1237 return implode(',', $pidList);
1238 }
1239
1240 /**
1241 * Returns a default value for a form field in the FORM cObject.
1242 * Page CANNOT be cached because that would include the inserted value for the current user.
1243 *
1244 * @param boolean $noValueInsert If noValueInsert OR if the no_cache flag for this page is NOT set, the original default value is returned.
1245 * @param string $fieldName The POST var name to get default value for
1246 * @param string $defaultVal The current default value
1247 * @return string The default value, either from INPUT var or the current default, based on whether caching is enabled or not.
1248 * @access private
1249 * @todo Define visibility
1250 */
1251 public function getFieldDefaultValue($noValueInsert, $fieldName, $defaultVal) {
1252 if (!$GLOBALS['TSFE']->no_cache || !isset($_POST[$fieldName]) && !isset($_GET[$fieldName]) || $noValueInsert) {
1253 return $defaultVal;
1254 } else {
1255 return GeneralUtility::_GP($fieldName);
1256 }
1257 }
1258
1259 /**
1260 * Returns a <img> tag with the image file defined by $file and processed according to the properties in the TypoScript array.
1261 * Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript.
1262 * This function is called by "$this->cImage($conf['file'], $conf);" from IMAGE().
1263 *
1264 * @param string $file File TypoScript resource
1265 * @param array $conf TypoScript configuration properties
1266 * @return string <img> tag, (possibly wrapped in links and other HTML) if any image found.
1267 * @access private
1268 * @see IMAGE()
1269 * @todo Define visibility
1270 */
1271 public function cImage($file, $conf) {
1272 $info = $this->getImgResource($file, $conf['file.']);
1273 $GLOBALS['TSFE']->lastImageInfo = $info;
1274 if (!is_array($info)) {
1275 return '';
1276 }
1277 if (is_file(PATH_site . $info['3'])) {
1278 $source = GeneralUtility::rawUrlEncodeFP(GeneralUtility::png_to_gif_by_imagemagick($info[3]));
1279 $source = $GLOBALS['TSFE']->absRefPrefix . $source;
1280 } else {
1281 $source = $info[3];
1282 }
1283
1284 $layoutKey = $this->stdWrap($conf['layoutKey'], $conf['layoutKey.']);
1285 $imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
1286 $sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
1287
1288 // This array is used to collect the image-refs on the page...
1289 $GLOBALS['TSFE']->imagesOnPage[] = $source;
1290 $altParam = $this->getAltParam($conf);
1291 $params = $this->stdWrapValue('params', $conf);
1292 if ($params !== '' && $params{0} !== ' ') {
1293 $params = ' ' . $params;
1294 }
1295
1296 $imageTagValues = array(
1297 'width' => $info[0],
1298 'height' => $info[1],
1299 'src' => htmlspecialchars($source),
1300 'params' => $params,
1301 'altParams' => $altParam,
1302 'border' => $this->getBorderAttr(' border="' . (int)$conf['border'] . '"'),
1303 'sourceCollection' => $sourceCollection,
1304 'selfClosingTagSlash' => (!empty($GLOBALS['TSFE']->xhtmlDoctype) ? ' /' : ''),
1305 );
1306
1307 $theValue = $this->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', TRUE, TRUE);
1308
1309 $linkWrap = isset($conf['linkWrap.']) ? $this->stdWrap($conf['linkWrap'], $conf['linkWrap.']) : $conf['linkWrap'];
1310 if ($linkWrap) {
1311 $theValue = $this->linkWrap($theValue, $linkWrap);
1312 } elseif ($conf['imageLinkWrap']) {
1313 $theValue = $this->imageLinkWrap($theValue, $info['originalFile'], $conf['imageLinkWrap.']);
1314 }
1315 $wrap = isset($conf['wrap.']) ? $this->stdWrap($conf['wrap'], $conf['wrap.']) : $conf['wrap'];
1316 if ($wrap) {
1317 $theValue = $this->wrap($theValue, $conf['wrap']);
1318 }
1319 return $theValue;
1320 }
1321
1322 /**
1323 * Returns the 'border' attribute for an <img> tag only if the doctype is not xhtml_strict, xhtml_11, xhtml_2 or html5
1324 * or if the config parameter 'disableImgBorderAttr' is not set.
1325 *
1326 * @param string $borderAttr The border attribute
1327 * @return string The border attribute
1328 * @todo Define visibility
1329 */
1330 public function getBorderAttr($borderAttr) {
1331 if (!GeneralUtility::inList('xhtml_strict,xhtml_11,xhtml_2', $GLOBALS['TSFE']->xhtmlDoctype) && $GLOBALS['TSFE']->config['config']['doctype'] != 'html5' && !$GLOBALS['TSFE']->config['config']['disableImgBorderAttr']) {
1332 return $borderAttr;
1333 }
1334 }
1335
1336 /**
1337 * Returns the html-template for rendering the image-Tag if no template is defined via typoscript the
1338 * default <img> tag template is returned
1339 *
1340 * @param string $layoutKey rendering key
1341 * @param array $conf TypoScript configuration properties
1342 * @return string
1343 */
1344 public function getImageTagTemplate($layoutKey, $conf) {
1345 if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
1346 $imageTagLayout = $this->stdWrap($conf['layout.'][$layoutKey . '.']['element'], $conf['layout.'][$layoutKey . '.']['element.']);
1347 } else {
1348 $imageTagLayout = '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###BORDER######SELFCLOSINGTAGSLASH###>';
1349 }
1350 return $imageTagLayout;
1351 }
1352
1353 /**
1354 * Render alternate sources for the image tag. If no source collection is given an empty string is returned.
1355 *
1356 * @param string $layoutKey rendering key
1357 * @param array $conf TypoScript configuration properties
1358 * @param string $file
1359 * @throws \UnexpectedValueException
1360 * @return string
1361 */
1362 public function getImageSourceCollection($layoutKey, $conf, $file) {
1363 $sourceCollection = '';
1364 if ($layoutKey && $conf['sourceCollection.'] && ($conf['layout.'][$layoutKey . '.']['source'] || $conf['layout.'][$layoutKey . '.']['source.'])) {
1365
1366 // find active sourceCollection
1367 $activeSourceCollections = array();
1368 foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
1369 if (substr($sourceCollectionKey, -1) == '.') {
1370 if (empty($sourceCollectionConfiguration['if.']) || $this->checkIf($sourceCollectionConfiguration['if.'])) {
1371 $activeSourceCollections[] = $sourceCollectionConfiguration;
1372 }
1373 }
1374 }
1375
1376 // apply option split to configurations
1377 $srcLayoutOptionSplitted = $GLOBALS['TSFE']->tmpl->splitConfArray($conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
1378
1379 // render sources
1380 foreach ($activeSourceCollections as $key => $sourceConfiguration) {
1381 $sourceLayout = $this->stdWrap($srcLayoutOptionSplitted[$key]['source'], $srcLayoutOptionSplitted[$key]['source.']);
1382
1383 $sourceRenderConfiguration = array (
1384 'file' => $file,
1385 'file.' => $conf['file.']
1386 );
1387
1388 if (isset($sourceConfiguration['pixelDensity'])) {
1389 $pixelDensity = (int)$this->stdWrap($sourceConfiguration['pixelDensity'], $sourceConfiguration['pixelDensity.']);
1390 } else {
1391 $pixelDensity = 1;
1392 }
1393 $dimensionKeys = array('width', 'height', 'maxW', 'minW', 'maxH', 'minH');
1394 foreach ($dimensionKeys as $dimensionKey) {
1395 $dimension = $this->stdWrap($sourceConfiguration[$dimensionKey], $sourceConfiguration[$dimensionKey . '.']);
1396 if (!$dimension) {
1397 $dimension = $this->stdWrap($conf['file.'][$dimensionKey], $conf['file.'][$dimensionKey . '.']);
1398 }
1399 if ($dimension) {
1400 if (strstr($dimension, 'c') !== FALSE && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
1401 $dimensionParts = explode('c', $dimension, 2);
1402 $dimension = (int)($dimensionParts[0] * $pixelDensity) . 'c';
1403 if ($dimensionParts[1]) {
1404 $dimension .= $dimensionParts[1];
1405 }
1406 } else {
1407 $dimension = (int)($dimension * $pixelDensity);
1408 }
1409 $sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
1410 // Remove the stdWrap properties for dimension as they have been processed already above.
1411 unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
1412 }
1413 }
1414 $sourceInfo = $this->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
1415 if ($sourceInfo) {
1416 $sourceConfiguration['width'] = $sourceInfo[0];
1417 $sourceConfiguration['height'] = $sourceInfo[1];
1418 $sourceConfiguration['src'] = htmlspecialchars($GLOBALS['TSFE']->absRefPrefix . $sourceInfo[3]);
1419 $sourceConfiguration['selfClosingTagSlash'] = (!empty($GLOBALS['TSFE']->xhtmlDoctype) ? ' /' : '');
1420
1421 $oneSourceCollection = $this->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', TRUE, TRUE);
1422
1423 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'])) {
1424 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getImageSourceCollection'] as $classData) {
1425 $hookObject = GeneralUtility::getUserObj($classData);
1426 if (!$hookObject instanceof ContentObjectOneSourceCollectionHookInterface) {
1427 throw new \UnexpectedValueException(
1428 '$hookObject must implement interface TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectOneSourceCollectionHookInterface',
1429 1380007853
1430 );
1431 }
1432 $oneSourceCollection = $hookObject->getOneSourceCollection((array) $sourceRenderConfiguration, (array) $sourceConfiguration, $oneSourceCollection, $this);
1433 }
1434 }
1435
1436 $sourceCollection .= $oneSourceCollection;
1437 }
1438 }
1439 }
1440 return $sourceCollection;
1441 }
1442
1443 /**
1444 * Wraps the input string in link-tags that opens the image in a new window.
1445 *
1446 * @param string $string String to wrap, probably an <img> tag
1447 * @param string|\TYPO3\CMS\Core\Resource\File|\TYPO3\CMS\Core\Resource\FileReference $imageFile The original image file
1448 * @param array $conf TypoScript properties for the "imageLinkWrap" function
1449 * @return string The input string, $string, wrapped as configured.
1450 * @see cImage()
1451 * @todo Define visibility
1452 */
1453 public function imageLinkWrap($string, $imageFile, $conf) {
1454 $a1 = '';
1455 $a2 = '';
1456 $content = $string;
1457 $enable = isset($conf['enable.']) ? $this->stdWrap($conf['enable'], $conf['enable.']) : $conf['enable'];
1458 if ($enable) {
1459 $content = $this->typolink($string, $conf['typolink.']);
1460 if (isset($conf['file.'])) {
1461 $imageFile = $this->stdWrap($imageFile, $conf['file.']);
1462 }
1463
1464 if ($imageFile instanceof \TYPO3\CMS\Core\Resource\File) {
1465 $file = $imageFile;
1466 } elseif ($imageFile instanceof \TYPO3\CMS\Core\Resource\FileReference) {
1467 $file = $imageFile->getOriginalFile();
1468 } else {
1469 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($imageFile)) {
1470 $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObject((int)$imageFile);
1471 } else {
1472 $file = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObjectFromCombinedIdentifier($imageFile);
1473 }
1474 }
1475
1476 // Create imageFileLink if not created with typolink
1477 if ($content == $string) {
1478 $parameterNames = array('width', 'height', 'effects', 'bodyTag', 'title', 'wrap');
1479 $parameters = array();
1480 $sample = isset($conf['sample.']) ? $this->stdWrap($conf['sample'], $conf['sample.']) : $conf['sample'];
1481 if ($sample) {
1482 $parameters['sample'] = 1;
1483 }
1484 foreach ($parameterNames as $parameterName) {
1485 if (isset($conf[$parameterName . '.'])) {
1486 $conf[$parameterName] = $this->stdWrap($conf[$parameterName], $conf[$parameterName . '.']);
1487 }
1488 if (isset($conf[$parameterName]) && $conf[$parameterName]) {
1489 $parameters[$parameterName] = $conf[$parameterName];
1490 }
1491 }
1492 $parametersEncoded = base64_encode(serialize($parameters));
1493 $hmac = GeneralUtility::hmac(implode('|', array($file->getUid(), $parametersEncoded)));
1494 $params = '&md5=' . $hmac;
1495 foreach (str_split($parametersEncoded, 64) as $index => $chunk) {
1496 $params .= '&parameters' . rawurlencode('[') . $index . rawurlencode(']') . '=' . rawurlencode($chunk);
1497 }
1498 $url = $GLOBALS['TSFE']->absRefPrefix . 'index.php?eID=tx_cms_showpic&file=' . $file->getUid() . $params;
1499 $directImageLink = isset($conf['directImageLink.']) ? $this->stdWrap($conf['directImageLink'], $conf['directImageLink.']) : $conf['directImageLink'];
1500 if ($directImageLink) {
1501 $imgResourceConf = array(
1502 'file' => $imageFile,
1503 'file.' => $conf
1504 );
1505 $url = $this->IMG_RESOURCE($imgResourceConf);
1506 if (!$url) {
1507 // If no imagemagick / gm is available
1508 $url = $imageFile;
1509 }
1510 }
1511 // Create TARGET-attribute only if the right doctype is used
1512 if (!GeneralUtility::inList('xhtml_strict,xhtml_11,xhtml_2', $GLOBALS['TSFE']->xhtmlDoctype)) {
1513 $target = isset($conf['target.']) ? $this->stdWrap($conf['target'], $conf['target.']) : $conf['target'];
1514 if ($target) {
1515 $target = sprintf(' target="%s"', $target);
1516 } else {
1517 $target = ' target="thePicture"';
1518 }
1519 } else {
1520 $target = '';
1521 }
1522 $conf['JSwindow'] = isset($conf['JSwindow.']) ? $this->stdWrap($conf['JSwindow'], $conf['JSwindow.']) : $conf['JSwindow'];
1523 if ($conf['JSwindow']) {
1524 if ($conf['JSwindow.']['altUrl'] || $conf['JSwindow.']['altUrl.']) {
1525 $altUrl = isset($conf['JSwindow.']['altUrl.']) ? $this->stdWrap($conf['JSwindow.']['altUrl'], $conf['JSwindow.']['altUrl.']) : $conf['JSwindow.']['altUrl'];
1526 if ($altUrl) {
1527 $url = $altUrl . ($conf['JSwindow.']['altUrl_noDefaultParams'] ? '' : '?file=' . rawurlencode($imageFile) . $params);
1528 }
1529 }
1530
1531 $processedFile = $file->process('Image.CropScaleMask', $conf);
1532 $JSwindowExpand = isset($conf['JSwindow.']['expand.']) ? $this->stdWrap($conf['JSwindow.']['expand'], $conf['JSwindow.']['expand.']) : $conf['JSwindow.']['expand'];
1533 $offset = GeneralUtility::intExplode(',', $JSwindowExpand . ',');
1534 $newWindow = isset($conf['JSwindow.']['newWindow.']) ? $this->stdWrap($conf['JSwindow.']['newWindow'], $conf['JSwindow.']['newWindow.']) : $conf['JSwindow.']['newWindow'];
1535 $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 . '>';
1536 $a2 = '</a>';
1537 $GLOBALS['TSFE']->setJS('openPic');
1538 } else {
1539 $conf['linkParams.']['parameter'] = $url;
1540 $string = $this->typoLink($string, $conf['linkParams.']);
1541 }
1542 if (isset($conf['stdWrap.'])) {
1543 $string = $this->stdWrap($string, $conf['stdWrap.']);
1544 }
1545 $content = $a1 . $string . $a2;
1546 }
1547 }
1548 return $content;
1549 }
1550
1551 /**
1552 * Returns content of a file. If it's an image the content of the file is not returned but rather an image tag is.
1553 *
1554 * @param string $fName The filename, being a TypoScript resource data type
1555 * @param string $addParams Additional parameters (attributes). Default is empty alt and title tags.
1556 * @return string If jpg,gif,jpeg,png: returns image_tag with picture in. If html,txt: returns content string
1557 * @see FILE()
1558 * @todo Define visibility
1559 */
1560 public function fileResource($fName, $addParams = 'alt="" title=""') {
1561 $incFile = $GLOBALS['TSFE']->tmpl->getFileName($fName);
1562 if ($incFile && file_exists($incFile)) {
1563 $fileinfo = GeneralUtility::split_fileref($incFile);
1564 if (GeneralUtility::inList('jpg,gif,jpeg,png', $fileinfo['fileext'])) {
1565 $imgFile = $incFile;
1566 $imgInfo = @getImageSize($imgFile);
1567 return '<img src="' . $GLOBALS['TSFE']->absRefPrefix . $imgFile . '" width="' . $imgInfo[0] . '" height="' . $imgInfo[1] . '"' . $this->getBorderAttr(' border="0"') . ' ' . $addParams . ' />';
1568 } elseif (filesize($incFile) < 1024 * 1024) {
1569 return $GLOBALS['TSFE']->tmpl->fileContent($incFile);
1570 }
1571 }
1572 }
1573
1574 /**
1575 * Sets the SYS_LASTCHANGED timestamp if input timestamp is larger than current value.
1576 * The SYS_LASTCHANGED timestamp can be used by various caching/indexing applications to determine if the page has new content.
1577 * Therefore you should call this function with the last-changed timestamp of any element you display.
1578 *
1579 * @param integer $tstamp Unix timestamp (number of seconds since 1970)
1580 * @return void
1581 * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::setSysLastChanged()
1582 * @todo Define visibility
1583 */
1584 public function lastChanged($tstamp) {
1585 $tstamp = (int)$tstamp;
1586 if ($tstamp > (int)$GLOBALS['TSFE']->register['SYS_LASTCHANGED']) {
1587 $GLOBALS['TSFE']->register['SYS_LASTCHANGED'] = $tstamp;
1588 }
1589 }
1590
1591 /**
1592 * Wraps the input string by the $wrap value and implements the "linkWrap" data type as well.
1593 * 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.
1594 *
1595 * @param string $content Input string
1596 * @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string
1597 * @return string Wrapped output string
1598 * @see wrap(), cImage(), FILE()
1599 * @todo Define visibility
1600 */
1601 public function linkWrap($content, $wrap) {
1602 $wrapArr = explode('|', $wrap);
1603 if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
1604 if ($uid = $GLOBALS['TSFE']->tmpl->rootLine[$reg[1]]['uid']) {
1605 $wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
1606 }
1607 }
1608 return trim($wrapArr[0]) . $content . trim($wrapArr[1]);
1609 }
1610
1611 /**
1612 * An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element.
1613 * From the $conf array it implements the properties "altText", "titleText" and "longdescURL"
1614 *
1615 * @param array $conf TypoScript configuration properties
1616 * @param boolean $longDesc If set, the longdesc attribute will be generated - must only be used for img elements!
1617 * @return string Parameter string containing alt and title parameters (if any)
1618 * @see IMGTEXT(), FILE(), FORM(), cImage(), filelink()
1619 * @todo Define visibility
1620 */
1621 public function getAltParam($conf, $longDesc = TRUE) {
1622 $altText = isset($conf['altText.']) ? trim($this->stdWrap($conf['altText'], $conf['altText.'])) : trim($conf['altText']);
1623 $titleText = isset($conf['titleText.']) ? trim($this->stdWrap($conf['titleText'], $conf['titleText.'])) : trim($conf['titleText']);
1624 if (isset($conf['longdescURL.']) && $GLOBALS['TSFE']->config['config']['doctype'] != 'html5') {
1625 $longDesc = $this->typoLink_URL($conf['longdescURL.']);
1626 } else {
1627 $longDesc = trim($conf['longdescURL']);
1628 }
1629 // "alt":
1630 $altParam = ' alt="' . htmlspecialchars($altText) . '"';
1631 // "title":
1632 $emptyTitleHandling = 'useAlt';
1633 $emptyTitleHandling = isset($conf['emptyTitleHandling.']) ? $this->stdWrap($conf['emptyTitleHandling'], $conf['emptyTitleHandling.']) : $conf['emptyTitleHandling'];
1634 // Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
1635 if ($titleText || $emptyTitleHandling == 'keepEmpty') {
1636 $altParam .= ' title="' . htmlspecialchars($titleText) . '"';
1637 } elseif (!$titleText && $emptyTitleHandling == 'useAlt') {
1638 $altParam .= ' title="' . htmlspecialchars($altText) . '"';
1639 }
1640 // "longDesc" URL
1641 if ($longDesc) {
1642 $altParam .= ' longdesc="' . htmlspecialchars(strip_tags($longDesc)) . '"';
1643 }
1644 return $altParam;
1645 }
1646
1647 /**
1648 * Removes forbidden characters and spaces from name/id attributes in the form tag and formfields
1649 *
1650 * @param string $name Input string
1651 * @return string the cleaned string
1652 * @see FORM()
1653 * @todo Define visibility
1654 */
1655 public function cleanFormName($name) {
1656 // Turn data[x][y] into data:x:y:
1657 $name = preg_replace('/\\[|\\]\\[?/', ':', trim($name));
1658 // Remove illegal chars like _
1659 return preg_replace('#[^:a-zA-Z0-9]#', '', $name);
1660 }
1661
1662 /**
1663 * An abstraction method to add parameters to an A tag.
1664 * Uses the ATagParams property.
1665 *
1666 * @param array $conf TypoScript configuration properties
1667 * @param boolean $addGlobal If set, will add the global config.ATagParams to the link
1668 * @return string String containing the parameters to the A tag (if non empty, with a leading space)
1669 * @see IMGTEXT(), filelink(), makelinks(), typolink()
1670 * @todo Define visibility
1671 */
1672 public function getATagParams($conf, $addGlobal = 1) {
1673 $aTagParams = '';
1674 if ($conf['ATagParams.']) {
1675 $aTagParams = ' ' . $this->stdWrap($conf['ATagParams'], $conf['ATagParams.']);
1676 } elseif ($conf['ATagParams']) {
1677 $aTagParams = ' ' . $conf['ATagParams'];
1678 }
1679 if ($addGlobal) {
1680 $aTagParams = ' ' . trim(($GLOBALS['TSFE']->ATagParams . $aTagParams));
1681 }
1682 // Extend params
1683 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'])) {
1684 $_params = array(
1685 'conf' => &$conf,
1686 'aTagParams' => &$aTagParams
1687 );
1688 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['getATagParamsPostProc'] as $objRef) {
1689 $processor =& GeneralUtility::getUserObj($objRef);
1690 $aTagParams = $processor->process($_params, $this);
1691 }
1692 }
1693
1694 $aTagParams = trim($aTagParams);
1695 if (!empty($aTagParams)) {
1696 $aTagParams = ' ' . $aTagParams;
1697 }
1698
1699 return $aTagParams;
1700 }
1701
1702 /**
1703 * All extension links should ask this function for additional properties to their tags.
1704 * Designed to add for instance an "onclick" property for site tracking systems.
1705 *
1706 * @param string $URL URL of the website
1707 * @param string $TYPE
1708 * @return string The additional tag properties
1709 * @todo Define visibility
1710 */
1711 public function extLinkATagParams($URL, $TYPE) {
1712 $out = '';
1713 if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']) {
1714 $extLinkATagParamsHandler = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['extLinkATagParamsHandler']);
1715 if (method_exists($extLinkATagParamsHandler, 'main')) {
1716 $out .= trim($extLinkATagParamsHandler->main($URL, $TYPE, $this));
1717 }
1718 }
1719 return trim($out) ? ' ' . trim($out) : '';
1720 }
1721
1722 /***********************************************
1723 *
1724 * HTML template processing functions
1725 *
1726 ***********************************************/
1727 /**
1728 * Returns a subpart from the input content stream.
1729 * A subpart is a part of the input stream which is encapsulated in a
1730 * string matching the input string, $marker. If this string is found
1731 * inside of HTML comment tags the start/end points of the content block
1732 * returned will be that right outside that comment block.
1733 * Example: The contennt string is
1734 * "Hello <!--###sub1### begin--> World. How are <!--###sub1### end--> you?"
1735 * If $marker is "###sub1###" then the content returned is
1736 * " World. How are ". The input content string could just as well have
1737 * been "Hello ###sub1### World. How are ###sub1### you?" and the result
1738 * would be the same
1739 * Wrapper for \TYPO3\CMS\Core\Html\HtmlParser::getSubpart which behaves identical
1740 *
1741 * @param string $content The content stream, typically HTML template content.
1742 * @param string $marker The marker string, typically on the form "###[the marker string]###
1743 * @return string The subpart found, if found.
1744 */
1745 public function getSubpart($content, $marker) {
1746 return \TYPO3\CMS\Core\Html\HtmlParser::getSubpart($content, $marker);
1747 }
1748
1749 /**
1750 * Substitute subpart in input template stream.
1751 * This function substitutes a subpart in $content with the content of
1752 * $subpartContent.
1753 * Wrapper for \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart which behaves identical
1754 *
1755 * @param string $content The content stream, typically HTML template content.
1756 * @param string $marker The marker string, typically on the form "###[the marker string]###
1757 * @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.
1758 * @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!
1759 * @return string The processed HTML content string.
1760 */
1761 public function substituteSubpart($content, $marker, $subpartContent, $recursive = 1) {
1762 return \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpart($content, $marker, $subpartContent, $recursive);
1763 }
1764
1765 /**
1766 * Substitues multiple subparts at once
1767 *
1768 * @param string $content The content stream, typically HTML template content.
1769 * @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.
1770 * @return string The processed HTML content string.
1771 */
1772 public function substituteSubpartArray($content, array $subpartsContent) {
1773 return \TYPO3\CMS\Core\Html\HtmlParser::substituteSubpartArray($content, $subpartsContent);
1774 }
1775
1776 /**
1777 * Substitutes a marker string in the input content
1778 * (by a simple str_replace())
1779 *
1780 * @param string $content The content stream, typically HTML template content.
1781 * @param string $marker The marker string, typically on the form "###[the marker string]###
1782 * @param mixed $markContent The content to insert instead of the marker string found.
1783 * @return string The processed HTML content string.
1784 * @see substituteSubpart()
1785 */
1786 public function substituteMarker($content, $marker, $markContent) {
1787 return \TYPO3\CMS\Core\Html\HtmlParser::substituteMarker($content, $marker, $markContent);
1788 }
1789
1790 /**
1791 * Multi substitution function with caching.
1792 *
1793 * This function should be a one-stop substitution function for working
1794 * with HTML-template. It does not substitute by str_replace but by
1795 * splitting. This secures that the value inserted does not themselves
1796 * contain markers or subparts.
1797 *
1798 * Note that the "caching" won't cache the content of the substition,
1799 * but only the splitting of the template in various parts. So if you
1800 * want only one cache-entry per template, make sure you always pass the
1801 * exact same set of marker/subpart keys. Else you will be flooding the
1802 * users cache table.
1803 *
1804 * This function takes three kinds of substitutions in one:
1805 * $markContentArray is a regular marker-array where the 'keys' are
1806 * substituted in $content with their values
1807 *
1808 * $subpartContentArray works exactly like markContentArray only is whole
1809 * subparts substituted and not only a single marker.
1810 *
1811 * $wrappedSubpartContentArray is an array of arrays with 0/1 keys where
1812 * the subparts pointed to by the main key is wrapped with the 0/1 value
1813 * alternating.
1814 *
1815 * @param string $content The content stream, typically HTML template content.
1816 * @param array $markContentArray Regular marker-array where the 'keys' are substituted in $content with their values
1817 * @param array $subpartContentArray Exactly like markContentArray only is whole subparts substituted and not only a single marker.
1818 * @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.
1819 * @return string The output content stream
1820 * @see substituteSubpart(), substituteMarker(), substituteMarkerInObject(), TEMPLATE()
1821 */
1822 public function substituteMarkerArrayCached($content, array $markContentArray = NULL, array $subpartContentArray = NULL, array $wrappedSubpartContentArray = NULL) {
1823 $GLOBALS['TT']->push('substituteMarkerArrayCached');
1824 // If not arrays then set them
1825 if (is_null($markContentArray)) {
1826 // Plain markers
1827 $markContentArray = array();
1828 }
1829 if (is_null($subpartContentArray)) {
1830 // Subparts being directly substituted
1831 $subpartContentArray = array();
1832 }
1833 if (is_null($wrappedSubpartContentArray)) {
1834 // Subparts being wrapped
1835 $wrappedSubpartContentArray = array();
1836 }
1837 // Finding keys and check hash:
1838 $sPkeys = array_keys($subpartContentArray);
1839 $wPkeys = array_keys($wrappedSubpartContentArray);
1840 $aKeys = array_merge(array_keys($markContentArray), $sPkeys, $wPkeys);
1841 if (!count($aKeys)) {
1842 $GLOBALS['TT']->pull();
1843 return $content;
1844 }
1845 asort($aKeys);
1846 $storeKey = md5('substituteMarkerArrayCached_storeKey:' . serialize(array(
1847 $content,
1848 $aKeys
1849 )));
1850 if ($this->substMarkerCache[$storeKey]) {
1851 $storeArr = $this->substMarkerCache[$storeKey];
1852 $GLOBALS['TT']->setTSlogMessage('Cached', 0);
1853 } else {
1854 $storeArrDat = $GLOBALS['TSFE']->sys_page->getHash($storeKey);
1855 if (is_array($storeArrDat)) {
1856 $storeArr = $storeArrDat;
1857 // Setting cache:
1858 $this->substMarkerCache[$storeKey] = $storeArr;
1859 $GLOBALS['TT']->setTSlogMessage('Cached from DB', 0);
1860 } else {
1861 // Initialize storeArr
1862 $storeArr = array();
1863 // Finding subparts and substituting them with the subpart as a marker
1864 foreach ($sPkeys as $sPK) {
1865 $content = $this->substituteSubpart($content, $sPK, $sPK);
1866 }
1867 // Finding subparts and wrapping them with markers
1868 foreach ($wPkeys as $wPK) {
1869 $content = $this->substituteSubpart($content, $wPK, array(
1870 $wPK,
1871 $wPK
1872 ));
1873 }
1874 // Traverse keys and quote them for reg ex.
1875 foreach ($aKeys as $tK => $tV) {
1876 $aKeys[$tK] = preg_quote($tV, '/');
1877 }
1878 $regex = '/' . implode('|', $aKeys) . '/';
1879 // Doing regex's
1880 $storeArr['c'] = preg_split($regex, $content);
1881 preg_match_all($regex, $content, $keyList);
1882 $storeArr['k'] = $keyList[0];
1883 // Setting cache:
1884 $this->substMarkerCache[$storeKey] = $storeArr;
1885 // Storing the cached data:
1886 $GLOBALS['TSFE']->sys_page->storeHash($storeKey, $storeArr, 'substMarkArrayCached');
1887 $GLOBALS['TT']->setTSlogMessage('Parsing', 0);
1888 }
1889 }
1890 // Substitution/Merging:
1891 // Merging content types together, resetting
1892 $valueArr = array_merge($markContentArray, $subpartContentArray, $wrappedSubpartContentArray);
1893 $wSCA_reg = array();
1894 $content = '';
1895 // Traversing the keyList array and merging the static and dynamic content
1896 foreach ($storeArr['k'] as $n => $keyN) {
1897 $content .= $storeArr['c'][$n];
1898 if (!is_array($valueArr[$keyN])) {
1899 $content .= $valueArr[$keyN];
1900 } else {
1901 $content .= $valueArr[$keyN][(int)$wSCA_reg[$keyN] % 2];
1902 $wSCA_reg[$keyN]++;
1903 }
1904 }
1905 $content .= $storeArr['c'][count($storeArr['k'])];
1906 $GLOBALS['TT']->pull();
1907 return $content;
1908 }
1909
1910 /**
1911 * Traverses the input $markContentArray array and for each key the marker
1912 * by the same name (possibly wrapped and in upper case) will be
1913 * substituted with the keys value in the array.
1914 *
1915 * This is very useful if you have a data-record to substitute in some
1916 * content. In particular when you use the $wrap and $uppercase values to
1917 * pre-process the markers. Eg. a key name like "myfield" could effectively
1918 * be represented by the marker "###MYFIELD###" if the wrap value
1919 * was "###|###" and the $uppercase boolean TRUE.
1920 *
1921 * @param string $content The content stream, typically HTML template content.
1922 * @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.
1923 * @param string $wrap A wrap value - [part 1] | [part 2] - for the markers before substitution
1924 * @param boolean $uppercase If set, all marker string substitution is done with upper-case markers.
1925 * @param boolean $deleteUnused If set, all unused marker are deleted.
1926 * @return string The processed output stream
1927 * @see substituteMarker(), substituteMarkerInObject(), TEMPLATE()
1928 */
1929 public function substituteMarkerArray($content, array $markContentArray, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
1930 return \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerArray($content, $markContentArray, $wrap, $uppercase, $deleteUnused);
1931 }
1932
1933 /**
1934 * Substitute marker array in an array of values
1935 *
1936 * @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)
1937 * @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.
1938 * @return mixed The processed input variable.
1939 * @see substituteMarker()
1940 */
1941 public function substituteMarkerInObject(&$tree, array $markContentArray) {
1942 if (is_array($tree)) {
1943 foreach ($tree as $key => $value) {
1944 $this->substituteMarkerInObject($tree[$key], $markContentArray);
1945 }
1946 } else {
1947 $tree = $this->substituteMarkerArray($tree, $markContentArray);
1948 }
1949 return $tree;
1950 }
1951
1952 /**
1953 * Replaces all markers and subparts in a template with the content provided in the structured array.
1954 *
1955 * @param string $content
1956 * @param array $markersAndSubparts
1957 * @param string $wrap
1958 * @param boolean $uppercase
1959 * @param boolean $deleteUnused
1960 * @return string
1961 */
1962 public function substituteMarkerAndSubpartArrayRecursive($content, array $markersAndSubparts, $wrap = '', $uppercase = FALSE, $deleteUnused = FALSE) {
1963 return \TYPO3\CMS\Core\Html\HtmlParser::substituteMarkerAndSubpartArrayRecursive($content, $markersAndSubparts, $wrap, $uppercase, $deleteUnused);
1964 }
1965
1966 /**
1967 * Adds elements to the input $markContentArray based on the values from
1968 * the fields from $fieldList found in $row
1969 *
1970 * @param array $markContentArray Array with key/values being marker-strings/substitution values.
1971 * @param array $row An array with keys found in the $fieldList (typically a record) which values should be moved to the $markContentArray
1972 * @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)
1973 * @param boolean $nl2br If set, all values added to $markContentArray will be nl2br()'ed
1974 * @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 "###
1975 * @param boolean $HSC If set, all values are passed through htmlspecialchars() - RECOMMENDED to avoid most obvious XSS and maintain XHTML compliance.
1976 * @return array The modified $markContentArray
1977 */
1978 public function fillInMarkerArray(array $markContentArray, array $row, $fieldList = '', $nl2br = TRUE, $prefix = 'FIELD_', $HSC = FALSE) {
1979 if ($fieldList) {
1980 $fArr = GeneralUtility::trimExplode(',', $fieldList, TRUE);
1981 foreach ($fArr as $field) {
1982 $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($row[$field], !empty($GLOBALS['TSFE']->xhtmlDoctype)) : $row[$field];
1983 }
1984 } else {
1985 if (is_array($row)) {
1986 foreach ($row as $field => $value) {
1987 if (!\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($field)) {
1988 if ($HSC) {
1989 $value = htmlspecialchars($value);
1990 }
1991 $markContentArray['###' . $prefix . $field . '###'] = $nl2br ? nl2br($value, !empty($GLOBALS['TSFE']->xhtmlDoctype)) : $value;
1992 }
1993 }
1994 }
1995 }
1996 return $markContentArray;
1997 }
1998
1999 /**
2000 * Sets the current file object during iterations over files.
2001 *
2002 * @param \TYPO3\CMS\Core\Resource\File $fileObject The file object.
2003 */
2004 public function setCurrentFile($fileObject) {
2005 $this->currentFile = $fileObject;
2006 }
2007
2008 /**
2009 * Gets the current file object during iterations over files.
2010 *
2011 * @return \TYPO3\CMS\Core\Resource\File The current file object.
2012 */
2013 public function getCurrentFile() {
2014 return $this->currentFile;
2015 }
2016
2017 /***********************************************
2018 *
2019 * "stdWrap" + sub functions
2020 *
2021 ***********************************************/
2022 /**
2023 * The "stdWrap" function. This is the implementation of what is known as "stdWrap properties" in TypoScript.
2024 * Basically "stdWrap" performs some processing of a value based on properties in the input $conf array(holding the TypoScript "stdWrap properties")
2025 * 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.
2026 *
2027 * If $this->alternativeData is an array it's used instead of the $this->data array in ->getData
2028 *
2029 * @param string $content Input value undergoing processing in this function. Possibly substituted by other values fetched from another source.
2030 * @param array $conf TypoScript "stdWrap properties".
2031 * @return string The processed input value
2032 */
2033 public function stdWrap($content = '', $conf = array()) {
2034 // If there is any hook object, activate all of the process and override functions.
2035 // The hook interface ContentObjectStdWrapHookInterface takes care that all 4 methods exist.
2036 if ($this->stdWrapHookObjects) {
2037 $conf['stdWrapPreProcess'] = 1;
2038 $conf['stdWrapOverride'] = 1;
2039 $conf['stdWrapProcess'] = 1;
2040 $conf['stdWrapPostProcess'] = 1;
2041 }
2042
2043 if (!is_array($conf) || !$conf) {
2044 return $content;
2045 }
2046
2047 // Cache handling
2048 if (is_array($conf['cache.'])) {
2049 $conf['cache.']['key'] = $this->stdWrap($conf['cache.']['key'], $conf['cache.']['key.']);
2050 $conf['cache.']['tags'] = $this->stdWrap($conf['cache.']['tags'], $conf['cache.']['tags.']);
2051 $conf['cache.']['lifetime'] = $this->stdWrap($conf['cache.']['lifetime'], $conf['cache.']['lifetime.']);
2052 $conf['cacheRead'] = 1;
2053 $conf['cacheStore'] = 1;
2054 }
2055 // Check, which of the available stdWrap functions is needed for the current conf Array
2056 // and keep only those but still in the same order
2057 $sortedConf = array_intersect_key($this->stdWrapOrder, $conf);
2058 // Functions types that should not make use of nested stdWrap function calls to avoid conflicts with internal TypoScript used by these functions
2059 $stdWrapDisabledFunctionTypes = 'cObject,functionName,stdWrap';
2060 // Additional Array to check whether a function has already been executed
2061 $isExecuted = array();
2062 // Additional switch to make sure 'required', 'if' and 'fieldRequired'
2063 // will still stop rendering immediately in case they return FALSE
2064 $this->stdWrapRecursionLevel++;
2065 $this->stopRendering[$this->stdWrapRecursionLevel] = FALSE;
2066 // execute each function in the predefined order
2067 foreach ($sortedConf as $stdWrapName => $functionType) {
2068 // eliminate the second key of a pair 'key'|'key.' to make sure functions get called only once and check if rendering has been stopped
2069 if (!$isExecuted[$stdWrapName] && !$this->stopRendering[$this->stdWrapRecursionLevel]) {
2070 $functionName = rtrim($stdWrapName, '.');
2071 $functionProperties = $functionName . '.';
2072 // If there is any code one the next level, check if it contains "official" stdWrap functions
2073 // if yes, execute them first - will make each function stdWrap aware
2074 // so additional stdWrap calls within the functions can be removed, since the result will be the same
2075 // exception: the recursive stdWrap function and cObject will still be using their own stdWrap call, since it modifies the content and not a property
2076 if (count($conf[$functionProperties]) && !GeneralUtility::inList($stdWrapDisabledFunctionTypes, $functionType)) {
2077 if (array_intersect_key($this->stdWrapOrder, $conf[$functionProperties])) {
2078 $conf[$functionName] = $this->stdWrap($conf[$functionName], $conf[$functionProperties]);
2079 }
2080 }
2081 // Check if key is still containing something, since it might have been changed by next level stdWrap before
2082 if ((isset($conf[$functionName]) || $conf[$functionProperties]) && ($functionType !== 'boolean' || $conf[$functionName])) {
2083 // Get just that part of $conf that is needed for the particular function
2084 $singleConf = array(
2085 $functionName => $conf[$functionName],
2086 $functionProperties => $conf[$functionProperties]
2087 );
2088 // In this special case 'spaceBefore' and 'spaceAfter' need additional stuff from 'space.''
2089 if ($functionName === 'spaceBefore' || $functionName === 'spaceAfter') {
2090 $singleConf['space.'] = $conf['space.'];
2091 }
2092 // Hand over the whole $conf array to the stdWrapHookObjects
2093 if ($functionType === 'hook') {
2094 $singleConf = $conf;
2095 }
2096 // Add both keys - with and without the dot - to the set of executed functions
2097 $isExecuted[$functionName] = TRUE;
2098 $isExecuted[$functionProperties] = TRUE;
2099 // Call the function with the prefix stdWrap_ to make sure nobody can execute functions just by adding their name to the TS Array
2100 $functionName = 'stdWrap_' . $functionName;
2101 $content = $this->{$functionName}($content, $singleConf);
2102 } elseif ($functionType == 'boolean' && !$conf[$functionName]) {
2103 $isExecuted[$functionName] = TRUE;
2104 $isExecuted[$functionProperties] = TRUE;
2105 }
2106 }
2107 }
2108 unset($this->stopRendering[$this->stdWrapRecursionLevel]);
2109 $this->stdWrapRecursionLevel--;
2110
2111 return $content;
2112 }
2113
2114 /**
2115 * Gets a configuration value by passing them through stdWrap first and taking a default value if stdWrap doesn't yield a result.
2116 *
2117 * @param string $key The config variable key (from TS array).
2118 * @param array $config The TypoScript array.
2119 * @param string $defaultValue Optional default value.
2120 * @return string Value of the config variable
2121 */
2122 public function stdWrapValue($key, array $config, $defaultValue = '') {
2123 if (isset($config[$key])) {
2124 if (!isset($config[$key . '.'])) {
2125 return $config[$key];
2126 }
2127 } elseif (isset($config[$key . '.'])) {
2128 $config[$key] = '';
2129 } else {
2130 return $defaultValue;
2131 }
2132 $stdWrapped = $this->stdWrap($config[$key], $config[$key . '.']);
2133 return $stdWrapped ?: $defaultValue;
2134 }
2135
2136 /**
2137 * stdWrap pre process hook
2138 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2139 * this hook will execute functions before any other stdWrap function can modify anything
2140 *
2141 * @param string $content Input value undergoing processing in these functions.
2142 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2143 * @return string The processed input value
2144 */
2145 public function stdWrap_stdWrapPreProcess($content = '', $conf = array()) {
2146 foreach ($this->stdWrapHookObjects as $hookObject) {
2147 $content = $hookObject->stdWrapPreProcess($content, $conf, $this);
2148 }
2149 return $content;
2150 }
2151
2152 /**
2153 * Check if content was cached before (depending on the given cache key)
2154 *
2155 * @param string $content Input value undergoing processing in these functions.
2156 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2157 * @return string The processed input value
2158 */
2159 public function stdWrap_cacheRead($content = '', $conf = array()) {
2160 if (!empty($conf['cache.']['key'])) {
2161 /** @var $cacheFrontend \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */
2162 $cacheFrontend = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager')->getCache('cache_hash');
2163 if ($cacheFrontend && $cacheFrontend->has($conf['cache.']['key'])) {
2164 $content = $cacheFrontend->get($conf['cache.']['key']);
2165 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2166 }
2167 }
2168 return $content;
2169 }
2170
2171 /**
2172 * Add tags to page cache (comma-separated list)
2173 *
2174 * @param string $content Input value undergoing processing in these functions.
2175 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2176 * @return string The processed input value
2177 */
2178 public function stdWrap_addPageCacheTags($content = '', $conf = array()) {
2179 $tags = isset($conf['addPageCacheTags.'])
2180 ? $this->stdWrap($conf['addPageCacheTags'], $conf['addPageCacheTags.'])
2181 : $conf['addPageCacheTags'];
2182 if (!empty($tags)) {
2183 $cacheTags = GeneralUtility::trimExplode(',', $tags, TRUE);
2184 $GLOBALS['TSFE']->addCacheTags($cacheTags);
2185 }
2186 return $content;
2187 }
2188
2189 /**
2190 * setContentToCurrent
2191 * actually it just does the contrary: Sets the value of 'current' based on current content
2192 *
2193 * @param string $content Input value undergoing processing in this function.
2194 * @param array $conf stdWrap properties for setContentToCurrent.
2195 * @return string The processed input value
2196 */
2197 public function stdWrap_setContentToCurrent($content = '', $conf = array()) {
2198 $this->data[$this->currentValKey] = $content;
2199 return $content;
2200 }
2201
2202 /**
2203 * setCurrent
2204 * Sets the value of 'current' based on the outcome of stdWrap operations
2205 *
2206 * @param string $content Input value undergoing processing in this function.
2207 * @param array $conf stdWrap properties for setCurrent.
2208 * @return string The processed input value
2209 */
2210 public function stdWrap_setCurrent($content = '', $conf = array()) {
2211 $this->data[$this->currentValKey] = $conf['setCurrent'];
2212 return $content;
2213 }
2214
2215 /**
2216 * lang
2217 * Translates content based on the language currently used by the FE
2218 *
2219 * @param string $content Input value undergoing processing in this function.
2220 * @param array $conf stdWrap properties for lang.
2221 * @return string The processed input value
2222 */
2223 public function stdWrap_lang($content = '', $conf = array()) {
2224 if (isset($conf['lang.']) && $GLOBALS['TSFE']->config['config']['language'] && isset($conf['lang.'][$GLOBALS['TSFE']->config['config']['language']])) {
2225 $content = $conf['lang.'][$GLOBALS['TSFE']->config['config']['language']];
2226 }
2227 return $content;
2228 }
2229
2230 /**
2231 * data
2232 * Gets content from different sources based on getText functions, makes use of alternativeData, when set
2233 *
2234 * @param string $content Input value undergoing processing in this function.
2235 * @param array $conf stdWrap properties for data.
2236 * @return string The processed input value
2237 */
2238 public function stdWrap_data($content = '', $conf = array()) {
2239 $content = $this->getData($conf['data'], is_array($this->alternativeData) ? $this->alternativeData : $this->data);
2240 // This must be unset directly after
2241 $this->alternativeData = '';
2242 return $content;
2243 }
2244
2245 /**
2246 * field
2247 * Gets content from a DB field
2248 *
2249 * @param string $content Input value undergoing processing in this function.
2250 * @param array $conf stdWrap properties for field.
2251 * @return string The processed input value
2252 */
2253 public function stdWrap_field($content = '', $conf = array()) {
2254 return $this->getFieldVal($conf['field']);
2255 }
2256
2257 /**
2258 * current
2259 * Gets content that has been perviously set as 'current'
2260 * Can be set via setContentToCurrent or setCurrent or will be set automatically i.e. inside the split function
2261 *
2262 * @param string $content Input value undergoing processing in this function.
2263 * @param array $conf stdWrap properties for current.
2264 * @return string The processed input value
2265 */
2266 public function stdWrap_current($content = '', $conf = array()) {
2267 return $this->data[$this->currentValKey];
2268 }
2269
2270 /**
2271 * cObject
2272 * Will replace the content with the value of a any official TypoScript cObject
2273 * like TEXT, COA, HMENU
2274 *
2275 * @param string $content Input value undergoing processing in this function.
2276 * @param array $conf stdWrap properties for cObject.
2277 * @return string The processed input value
2278 */
2279 public function stdWrap_cObject($content = '', $conf = array()) {
2280 return $this->cObjGetSingle($conf['cObject'], $conf['cObject.'], '/stdWrap/.cObject');
2281 }
2282
2283 /**
2284 * numRows
2285 * Counts the number of returned records of a DB operation
2286 * makes use of select internally
2287 *
2288 * @param string $content Input value undergoing processing in this function.
2289 * @param array $conf stdWrap properties for numRows.
2290 * @return string The processed input value
2291 */
2292 public function stdWrap_numRows($content = '', $conf = array()) {
2293 return $this->numRows($conf['numRows.']);
2294 }
2295
2296 /**
2297 * filelist
2298 * Will create a list of files based on some additional parameters
2299 *
2300 * @param string $content Input value undergoing processing in this function.
2301 * @param array $conf stdWrap properties for filelist.
2302 * @return string The processed input value
2303 */
2304 public function stdWrap_filelist($content = '', $conf = array()) {
2305 return $this->filelist($conf['filelist']);
2306 }
2307
2308 /**
2309 * preUserFunc
2310 * Will execute a user public function before the content will be modified by any other stdWrap function
2311 *
2312 * @param string $content Input value undergoing processing in this function.
2313 * @param array $conf stdWrap properties for preUserFunc.
2314 * @return string The processed input value
2315 */
2316 public function stdWrap_preUserFunc($content = '', $conf = array()) {
2317 return $this->callUserFunction($conf['preUserFunc'], $conf['preUserFunc.'], $content);
2318 }
2319
2320 /**
2321 * stdWrap override hook
2322 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2323 * this hook will execute functions on existing content but still before the content gets modified or replaced
2324 *
2325 * @param string $content Input value undergoing processing in these functions.
2326 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2327 * @return string The processed input value
2328 */
2329 public function stdWrap_stdWrapOverride($content = '', $conf = array()) {
2330 foreach ($this->stdWrapHookObjects as $hookObject) {
2331 $content = $hookObject->stdWrapOverride($content, $conf, $this);
2332 }
2333 return $content;
2334 }
2335
2336 /**
2337 * override
2338 * Will override the current value of content with its own value'
2339 *
2340 * @param string $content Input value undergoing processing in this function.
2341 * @param array $conf stdWrap properties for override.
2342 * @return string The processed input value
2343 */
2344 public function stdWrap_override($content = '', $conf = array()) {
2345 if (trim($conf['override'])) {
2346 $content = $conf['override'];
2347 }
2348 return $content;
2349 }
2350
2351 /**
2352 * preIfEmptyListNum
2353 * Gets a value off a CSV list before the following ifEmpty check
2354 * 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
2355 *
2356 * @param string $content Input value undergoing processing in this function.
2357 * @param array $conf stdWrap properties for preIfEmptyListNum.
2358 * @return string The processed input value
2359 */
2360 public function stdWrap_preIfEmptyListNum($content = '', $conf = array()) {
2361 return $this->listNum($content, $conf['preIfEmptyListNum'], $conf['preIfEmptyListNum.']['splitChar']);
2362 }
2363
2364 /**
2365 * ifNull
2366 * Will set content to a replacement value in case the value of content is NULL
2367 *
2368 * @param string|NULL $content Input value undergoing processing in this function.
2369 * @param array $conf stdWrap properties for ifNull.
2370 * @return string The processed input value
2371 */
2372 public function stdWrap_ifNull($content = '', $conf = array()) {
2373 return $content !== NULL ? $content : $conf['ifNull'];
2374 }
2375
2376 /**
2377 * ifEmpty
2378 * Will set content to a replacement value in case the trimmed value of content returns FALSE
2379 * 0 (zero) will be replaced as well
2380 *
2381 * @param string $content Input value undergoing processing in this function.
2382 * @param array $conf stdWrap properties for ifEmpty.
2383 * @return string The processed input value
2384 */
2385 public function stdWrap_ifEmpty($content = '', $conf = array()) {
2386 if (!trim($content)) {
2387 $content = $conf['ifEmpty'];
2388 }
2389 return $content;
2390 }
2391
2392 /**
2393 * ifBlank
2394 * Will set content to a replacement value in case the trimmed value of content has no length
2395 * 0 (zero) will not be replaced
2396 *
2397 * @param string $content Input value undergoing processing in this function.
2398 * @param array $conf stdWrap properties for ifBlank.
2399 * @return string The processed input value
2400 */
2401 public function stdWrap_ifBlank($content = '', $conf = array()) {
2402 if (!strlen(trim($content))) {
2403 $content = $conf['ifBlank'];
2404 }
2405 return $content;
2406 }
2407
2408 /**
2409 * listNum
2410 * Gets a value off a CSV list after ifEmpty check
2411 * Might return an empty value in case the CSV does not contain a value at the position given by listNum
2412 * Use preIfEmptyListNum to avoid that behaviour
2413 *
2414 * @param string $content Input value undergoing processing in this function.
2415 * @param array $conf stdWrap properties for listNum.
2416 * @return string The processed input value
2417 */
2418 public function stdWrap_listNum($content = '', $conf = array()) {
2419 return $this->listNum($content, $conf['listNum'], $conf['listNum.']['splitChar']);
2420 }
2421
2422 /**
2423 * trim
2424 * Cuts off any whitespace at the beginning and the end of the content
2425 *
2426 * @param string $content Input value undergoing processing in this function.
2427 * @param array $conf stdWrap properties for trim.
2428 * @return string The processed input value
2429 */
2430 public function stdWrap_trim($content = '', $conf = array()) {
2431 return trim($content);
2432 }
2433
2434 /**
2435 * strPad
2436 * Will return a string padded left/right/on both sides, based on configuration given as stdWrap properties
2437 *
2438 * @param string $content Input value undergoing processing in this function.
2439 * @param array $conf stdWrap properties for strPad.
2440 * @return string The processed input value
2441 */
2442 public function stdWrap_strPad($content = '', $conf = array()) {
2443 // Must specify a length in conf for this to make sense
2444 $length = 0;
2445 // Padding with space is PHP-default
2446 $padWith = ' ';
2447 // Padding on the right side is PHP-default
2448 $padType = STR_PAD_RIGHT;
2449 if (!empty($conf['strPad.']['length'])) {
2450 $length = isset($conf['strPad.']['length.']) ? $this->stdWrap($conf['strPad.']['length'], $conf['strPad.']['length.']) : $conf['strPad.']['length'];
2451 $length = (int)$length;
2452 }
2453 if (isset($conf['strPad.']['padWith']) && strlen($conf['strPad.']['padWith']) > 0) {
2454 $padWith = isset($conf['strPad.']['padWith.']) ? $this->stdWrap($conf['strPad.']['padWith'], $conf['strPad.']['padWith.']) : $conf['strPad.']['padWith'];
2455 }
2456 if (!empty($conf['strPad.']['type'])) {
2457 $type = isset($conf['strPad.']['type.']) ? $this->stdWrap($conf['strPad.']['type'], $conf['strPad.']['type.']) : $conf['strPad.']['type'];
2458 if (strtolower($type) === 'left') {
2459 $padType = STR_PAD_LEFT;
2460 } elseif (strtolower($type) === 'both') {
2461 $padType = STR_PAD_BOTH;
2462 }
2463 }
2464 $content = str_pad($content, $length, $padWith, $padType);
2465 return $content;
2466 }
2467
2468 /**
2469 * stdWrap
2470 * A recursive call of the stdWrap function set
2471 * This enables the user to execute stdWrap functions in another than the predefined order
2472 * It modifies the content, not the property
2473 * while the new feature of chained stdWrap functions modifies the property and not the content
2474 *
2475 * @param string $content Input value undergoing processing in this function.
2476 * @param array $conf stdWrap properties for stdWrap.
2477 * @return string The processed input value
2478 */
2479 public function stdWrap_stdWrap($content = '', $conf = array()) {
2480 return $this->stdWrap($content, $conf['stdWrap.']);
2481 }
2482
2483 /**
2484 * stdWrap process hook
2485 * can be used by extensions authors to modify the behaviour of stdWrap functions to their needs
2486 * this hook executes functions directly after the recursive stdWrap function call but still before the content gets modified
2487 *
2488 * @param string $content Input value undergoing processing in these functions.
2489 * @param array $conf All stdWrap properties, not just the ones for a particular function.
2490 * @return string The processed input value
2491 */
2492 public function stdWrap_stdWrapProcess($content = '', $conf = array()) {
2493 foreach ($this->stdWrapHookObjects as $hookObject) {
2494 $content = $hookObject->stdWrapProcess($content, $conf, $this);
2495 }
2496 return $content;
2497 }
2498
2499 /**
2500 * required
2501 * Will immediately stop rendering and return an empty value
2502 * when there is no content at this point
2503 *
2504 * @param string $content Input value undergoing processing in this function.
2505 * @param array $conf stdWrap properties for required.
2506 * @return string The processed input value
2507 */
2508 public function stdWrap_required($content = '', $conf = array()) {
2509 if ((string) $content === '') {
2510 $content = '';
2511 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2512 }
2513 return $content;
2514 }
2515
2516 /**
2517 * if
2518 * Will immediately stop rendering and return an empty value
2519 * when the result of the checks returns FALSE
2520 *
2521 * @param string $content Input value undergoing processing in this function.
2522 * @param array $conf stdWrap properties for if.
2523 * @return string The processed input value
2524 */
2525 public function stdWrap_if($content = '', $conf = array()) {
2526 if (empty($conf['if.']) || $this->checkIf($conf['if.'])) {
2527 return $content;
2528 }
2529 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2530 return '';
2531 }
2532
2533 /**
2534 * fieldRequired
2535 * Will immediately stop rendering and return an empty value
2536 * when there is no content in the field given by fieldRequired
2537 *
2538 * @param string $content Input value undergoing processing in this function.
2539 * @param array $conf stdWrap properties for fieldRequired.
2540 * @return string The processed input value
2541 */
2542 public function stdWrap_fieldRequired($content = '', $conf = array()) {
2543 if (!trim($this->data[$conf['fieldRequired']])) {
2544 $content = '';
2545 $this->stopRendering[$this->stdWrapRecursionLevel] = TRUE;
2546 }
2547 return $content;
2548 }
2549
2550 /**
2551 * csConv
2552 * Will convert the current chracter set of the content to the one given in csConv
2553 *
2554 * @param string $content Input value undergoing processing in this function.
2555 * @param array $conf stdWrap properties for csConv.
2556 * @return string The processed input value
2557 */
2558 public function stdWrap_csConv($content = '', $conf = array()) {
2559 return $GLOBALS['TSFE']->csConv($content, $conf['csConv']);
2560 }
2561
2562 /**
2563 * parseFunc
2564 * Will parse the content based on functions given as stdWrap properties
2565 * Heavily used together with RTE based content
2566 *
2567 * @param string $content Input value undergoing processing in this function.
2568 * @param array $conf stdWrap properties for parseFunc.
2569 * @return string The processed input value
2570 */
2571 public function stdWrap_parseFunc($content = '', $conf = array()) {
2572 return $this->parseFunc($content, $conf['parseFunc.'], $conf['parseFunc']);
2573 }
2574
2575 /**
2576 * HTMLparser
2577 * Will parse HTML content based on functions given as stdWrap properties
2578 * Heavily used together with RTE based content
2579 *
2580 * @param string $content Input value undergoing processing in this function.
2581 * @param array $conf stdWrap properties for HTMLparser.
2582 * @return string The processed input value
2583 */
2584 public function stdWrap_HTMLparser($content = '', $conf = array()) {
2585 if (is_array($conf['HTMLparser.'])) {
2586 $content = $this->HTMLparser_TSbridge($content, $conf['HTMLparser.']);
2587 }
2588 return $content;
2589 }
2590
2591 /**
2592 * split
2593 * Will split the content by a given token and treat the results separately
2594 * Automatically fills 'current' with a single result
2595 *
2596 * @param string $content Input value undergoing processing in this function.
2597 * @param array $conf stdWrap properties for split.
2598 * @return string The processed input value
2599 */
2600 public function stdWrap_split($content = '', $conf = array()) {
2601 return $this->splitObj($content, $conf['split.']);
2602 }
2603
2604 /**
2605 * replacement
2606 * Will execute replacements on the content (optionally with preg-regex)
2607 *
2608 * @param string $content Input value undergoing processing in this function.
2609 * @param array $conf stdWrap properties for replacement.
2610 * @return string The processed input value
2611 */
2612 public function stdWrap_replacement($content = '', $conf = array()) {
2613 return $this->replacement($content, $conf['replacement.']);
2614 }
2615
2616 /**
2617 * prioriCalc
2618 * Will use the content as a mathematical term and calculate the result
2619 * Can be set to 1 to just get a calculated value or 'intval' to get the integer of the result
2620 *
2621 * @param string $content Input value undergoing processing in this function.
2622 * @param array $conf stdWrap properties for prioriCalc.
2623 * @return string The processed input value
2624 */
2625 public function stdWrap_prioriCalc($content = '', $conf = array()) {
2626 $content = \TYPO3\CMS\Core\Utility\MathUtility::calculateWithParentheses($content);
2627 if ($conf['prioriCalc'] == 'intval') {
2628 $content = (int)$content;
2629 }
2630 return $content;
2631 }
2632
2633 /**
2634 * char
2635 * Will return a character based on its position within the current character set
2636 *
2637 * @param string $content Input value undergoing processing in this function.
2638 * @param array $conf stdWrap properties for char.
2639 * @return string The processed input value
2640 */
2641 public function stdWrap_char($content = '', $conf = array()) {
2642 return chr((int)$conf['char']);
2643 }
2644
2645 /**
2646 * intval
2647 * Will return an integer value of the current content
2648 *
2649 * @param string $content Input value undergoing processing in this function.
2650 * @param array $conf stdWrap properties for intval.
2651 * @return string The processed input value
2652 */
2653 public function stdWrap_intval($content = '', $conf = array()) {
2654 return (int)$content;
2655 }
2656
2657 /**
2658 * Will return a hashed value of the current content
2659 *
2660 * @param string $content Input value undergoing processing in this function.
2661 * @param array $conf stdWrap properties for hash.
2662 * @return string The processed input value
2663 * @link http://php.net/manual/de/function.hash-algos.php for a list of supported hash algorithms
2664 */
2665 public function stdWrap_hash($content = '', array $conf = array()) {
2666 $algorithm = isset($conf['hash.']) ? $this->stdWrap($conf['hash'], $conf['hash.']) : $conf['hash'];
2667 if (function_exists('hash') && in_array($algorithm, hash_algos())) {
2668 $content = hash($algorithm, $content);
2669 } else {
2670 // Non-existing hashing algorithm
2671 $content = '';
2672 }
2673 return $content;
2674 }
2675
2676 /**
2677 * stdWrap_round will return a rounded number with ceil(), floor() or round(), defaults to round()
2678 * Only the english number format is supported . (dot) as decimal point
2679 *
2680 * @param string $content Input value undergoing processing in this function.
2681 * @param array $conf stdWrap properties for round.
2682 * @return string The processed input value
2683 */
2684 public function stdWrap_round($content = '', $conf = array()) {
2685 return $this->round($content, $conf['round.']);
2686 }
2687
2688 /**
2689 * numberFormat
2690 * Will return a formatted number based on configuration given as stdWrap properties
2691 *
2692 * @param string $content Input value undergoing processing in this function.
2693 * @param array $conf stdWrap properties for numberFormat.
2694 * @return string The processed input value
2695 */
2696 public function stdWrap_numberFormat($content = '', $conf = array()) {
2697 return $this->numberFormat($content, $conf['numberFormat.']);
2698 }
2699
2700 /**
2701 * expandList
2702 * Will return a formatted number based on configuration given as stdWrap properties
2703 *
2704 * @param string $content Input value undergoing processing in this function.
2705 * @param array $conf stdWrap properties for expandList.
2706 * @return string The processed input value
2707 */
2708 public function stdWrap_expandList($content = '', $conf = array()) {
2709 return GeneralUtility::expandList($content);
2710 }
2711
2712 /**
2713 * date
2714 * Will return a formatted date based on configuration given according to PHP date/gmdate properties
2715 * Will return gmdate when the property GMT returns TRUE
2716 *
2717 * @param string $content Input value undergoing processing in this function.
2718 * @param array $conf stdWrap properties for date.
2719 * @return string The processed input value
2720 */
2721 public function stdWrap_date($content = '', $conf = array()) {
2722 // Check for zero length string to mimic default case of date/gmdate.
2723 $content = $content == '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
2724 $content = $conf['date.']['GMT'] ? gmdate($conf['date'], $content) : date($conf['date'], $content);
2725 return $content;
2726 }
2727
2728 /**
2729 * strftime
2730 * Will return a formatted date based on configuration given according to PHP strftime/gmstrftime properties
2731 * Will return gmstrftime when the property GMT returns TRUE
2732 *
2733 * @param string $content Input value undergoing processing in this function.
2734 * @param array $conf stdWrap properties for strftime.
2735 * @return string The processed input value
2736 */
2737 public function stdWrap_strftime($content = '', $conf = array()) {
2738 // Check for zero length string to mimic default case of strtime/gmstrftime
2739 $content = $content == '' ? $GLOBALS['EXEC_TIME'] : (int)$content;
2740 $content = $conf['strftime.']['GMT'] ? gmstrftime($conf['strftime'], $content) : strftime($conf['strftime'], $content);
2741 $tmp_charset = $conf['strftime.']['charset'] ? $conf['strftime.']['charset'] : $GLOBALS['TSFE']->localeCharset;
2742 if ($tmp_charset) {
2743 $content = $GLOBALS['TSFE']->csConv($content, $tmp_charset);
2744 }
2745 return $content;
2746 }
2747
2748 /**
2749 * age
2750 * Will return the age of a given timestamp based on configuration given by stdWrap properties
2751 *
2752 * @param string $content Input value undergoing processing in this function.
2753 * @param array $conf stdWrap properties for age.
2754 * @return string The processed input value
2755 */
2756 public function stdWrap_age($content = '', $conf = array()) {
2757 return $this->calcAge($GLOBALS['EXEC_TIME'] - $content, $conf['age']);
2758 }
2759
2760 /**
2761 * case
2762 * Will transform the content to be upper or lower case only
2763 * Leaves HTML tags untouched
2764 *
2765 * @param string $content Input value undergoing processing in this function.
2766 * @param array $conf stdWrap properties for case.
2767 * @return string The processed input value
2768 */
2769 public function stdWrap_case($content = '', $conf = array()) {
2770 return $this->HTMLcaseshift($content, $conf['case']);
2771 }
2772
2773 /**
2774 * bytes
2775 * Will return the size of a given number in Bytes *
2776 *
2777 * @param string $content Input value undergoing processing in this function.
2778 * @param array $conf stdWrap properties for bytes.
2779 * @return string The processed input value
2780 */
2781 public function stdWrap_bytes($content = '', $conf = array()) {
2782 return GeneralUtility::formatSize($content, $conf['bytes.']['labels']);
2783 }
2784
2785 /**
2786 * substring
2787 * Will return a substring based on position information given by stdWrap properties
2788 *
2789 * @param string $content Input value undergoing processing in this function.
2790 * @param array $conf stdWrap properties for substring.
2791 * @return string The processed input value
2792 */
2793 public function stdWrap_substring($content = '', $conf = array()) {
2794 return $this->substring($content, $conf['substring']);
2795 }
2796
2797 /**
2798 * removeBadHTML
2799 * Removes HTML tags based on stdWrap properties
2800 *
2801 * @param string $content Input value undergoing processing in this function.
2802 * @param array $conf stdWrap properties for removeBadHTML.
2803 * @return string The processed input value
2804 */
2805 public function stdWrap_removeBadHTML($content = '', $conf = array()) {
2806 return $this->removeBadHTML($content, $conf['removeBadHTML.']);
2807 }
2808
2809 /**
2810 * cropHTML
2811 * Crops content to a given size while leaving HTML tags untouched
2812 *
2813 * @param string $content Input value undergoing processing in this function.
2814 * @param array $conf stdWrap properties for cropHTML.
2815 * @return string The processed input value
2816 */
2817 public function stdWrap_cropHTML($content = '', $conf = array()) {
2818 return $this->cropHTML($content, $conf['cropHTML']);
2819 }
2820
2821 /**
2822 * stripHtml
2823 * Copmletely removes HTML tags from content
2824 *
2825 * @param string $content Input value undergoing processing in this function.
2826 * @param array $conf stdWrap properties for stripHtml.
2827 * @return string The processed input value
2828 */
2829 public function stdWrap_stripHtml($content = '', $conf = array()) {
2830 return strip_tags($content);
2831 }
2832
2833 /**
2834 * crop
2835 * Crops content to a given size without caring about HTML tags
2836 *
2837 * @param string $content Input value undergoing processing in this function.
2838 * @param array $conf stdWrap properties for crop.
2839 * @return string The processed input value
2840 */
2841 public function stdWrap_crop($content = '', $conf = array()) {
2842 return $this->crop($content, $conf['crop']);
2843 }
2844
2845 /**
2846 * rawUrlEncode
2847 * Encodes content to be used within URLs
2848 *
2849 * @param string $content Input value undergoing processing in this function.
2850 * @param array $conf stdWrap properties for rawUrlEncode.
2851 * @return string The processed input value
2852 */
2853 public function stdWrap_rawUrlEncode($content = '', $conf = array()) {
2854 return rawurlencode($content);
2855 }
2856
2857 /**
2858 * htmlSpecialChars
2859 * Transforms HTML tags to readable text by replacing special characters with their HTML entity
2860 * When preserveEntities returns TRUE, existing entities will be left untouched
2861 *
2862 * @param string $content Input value undergoing processing in this function.
2863 * @param array $conf stdWrap properties for htmlSpecalChars.
2864 * @return string The processed input value
2865 */
2866 public function stdWrap_htmlSpecialChars($content = '', $conf = array()) {
2867 if (!empty($conf['htmlSpecialChars.']['preserveEntities'])) {
2868 $content = htmlspecialchars($content, ENT_COMPAT, 'UTF-8', FALSE);
2869 } else {
2870 $content = htmlspecialchars($content);
2871 }
2872 return $content;
2873 }
2874
2875 /**
2876 * encodeForJavaScriptValue
2877 * Escapes content to be used inside JavaScript strings. No quotes are added around the value
2878 * as this can easily be done in TypoScript
2879 *
2880 * @param string $content Input value undergoing processing in this function
2881 * @param array $conf stdWrap properties for encodeForJavaScriptValue
2882 * @return string The processed input value
2883 */
2884 public function stdWrap_encodeForJavaScriptValue($content = '', $conf = array()) {
2885 return GeneralUtility::quoteJSvalue($content);
2886 }
2887