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