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