[TASK] Use null coalescing operator where possible
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / ViewHelpers / CObjectViewHelper.php
1 <?php
2 namespace TYPO3\CMS\Fluid\ViewHelpers;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
19 use TYPO3\CMS\Extbase\Object\ObjectManager;
20 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
21 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
22 use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
23 use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
24
25 /**
26 * This ViewHelper renders CObjects from the global TypoScript configuration.
27 * NOTE: You have to ensure proper escaping (htmlspecialchars/intval/etc.) on your own!
28 *
29 * = Examples =
30 *
31 * <code title="Render lib object">
32 * <f:cObject typoscriptObjectPath="lib.someLibObject" />
33 * </code>
34 * <output>
35 * rendered lib.someLibObject
36 * </output>
37 *
38 * <code title="Specify cObject data & current value">
39 * <f:cObject typoscriptObjectPath="lib.customHeader" data="{article}" currentValueKey="title" />
40 * </code>
41 * <output>
42 * rendered lib.customHeader. data and current value will be available in TypoScript
43 * </output>
44 *
45 * <code title="inline notation">
46 * {article -> f:cObject(typoscriptObjectPath: 'lib.customHeader')}
47 * </code>
48 * <output>
49 * rendered lib.customHeader. data will be available in TypoScript
50 * </output>
51 */
52 class CObjectViewHelper extends AbstractViewHelper
53 {
54 use CompileWithContentArgumentAndRenderStatic;
55
56 /**
57 * Disable escaping of child nodes' output
58 *
59 * @var bool
60 */
61 protected $escapeChildren = false;
62
63 /**
64 * Disable escaping of this node's output
65 *
66 * @var bool
67 */
68 protected $escapeOutput = false;
69
70 /**
71 * @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController contains a backup of the current $GLOBALS['TSFE'] if used in BE mode
72 */
73 protected static $tsfeBackup;
74
75 /**
76 * Initialize arguments.
77 *
78 * @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
79 */
80 public function initializeArguments()
81 {
82 $this->registerArgument('data', 'mixed', 'the data to be used for rendering the cObject. Can be an object, array or string. If this argument is not set, child nodes will be used');
83 $this->registerArgument('typoscriptObjectPath', 'string', 'the TypoScript setup path of the TypoScript object to render', true);
84 $this->registerArgument('currentValueKey', 'string', 'currentValueKey');
85 $this->registerArgument('table', 'string', 'the table name associated with "data" argument. Typically tt_content or one of your custom tables. This argument should be set if rendering a FILES cObject where file references are used, or if the data argument is a database record.', false, '');
86 }
87
88 /**
89 * Renders the TypoScript object in the given TypoScript setup path.
90 *
91 * @param array $arguments
92 * @param \Closure $renderChildrenClosure
93 * @param RenderingContextInterface $renderingContext
94 * @return mixed
95 * @throws \TYPO3\CMS\Fluid\Core\ViewHelper\Exception
96 */
97 public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
98 {
99 $data = $renderChildrenClosure();
100 $typoscriptObjectPath = $arguments['typoscriptObjectPath'];
101 $currentValueKey = $arguments['currentValueKey'];
102 $table = $arguments['table'];
103 $contentObjectRenderer = static::getContentObjectRenderer();
104 if (TYPO3_MODE === 'BE') {
105 static::simulateFrontendEnvironment();
106 }
107 $currentValue = null;
108 if (is_object($data)) {
109 $data = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getGettableProperties($data);
110 } elseif (is_string($data) || is_numeric($data)) {
111 $currentValue = (string)$data;
112 $data = [$data];
113 }
114 $contentObjectRenderer->start($data, $table);
115 if ($currentValue !== null) {
116 $contentObjectRenderer->setCurrentVal($currentValue);
117 } elseif ($currentValueKey !== null && isset($data[$currentValueKey])) {
118 $contentObjectRenderer->setCurrentVal($data[$currentValueKey]);
119 }
120 $pathSegments = GeneralUtility::trimExplode('.', $typoscriptObjectPath);
121 $lastSegment = array_pop($pathSegments);
122 $setup = static::getConfigurationManager()->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT);
123 foreach ($pathSegments as $segment) {
124 if (!array_key_exists(($segment . '.'), $setup)) {
125 throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('TypoScript object path "' . htmlspecialchars($typoscriptObjectPath) . '" does not exist', 1253191023);
126 }
127 $setup = $setup[$segment . '.'];
128 }
129 $content = $contentObjectRenderer->cObjGetSingle($setup[$lastSegment], $setup[$lastSegment . '.']);
130 if (TYPO3_MODE === 'BE') {
131 static::resetFrontendEnvironment();
132 }
133 return $content;
134 }
135
136 /**
137 * @return ConfigurationManagerInterface
138 */
139 protected static function getConfigurationManager()
140 {
141 return GeneralUtility::makeInstance(ObjectManager::class)->get(ConfigurationManagerInterface::class);
142 }
143
144 /**
145 * @return ContentObjectRenderer
146 */
147 protected static function getContentObjectRenderer()
148 {
149 return $GLOBALS['TSFE']->cObj;
150 }
151
152 /**
153 * Sets the $TSFE->cObjectDepthCounter in Backend mode
154 * This somewhat hacky work around is currently needed because the cObjGetSingle() function of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer relies on this setting
155 */
156 protected static function simulateFrontendEnvironment()
157 {
158 static::$tsfeBackup = $GLOBALS['TSFE'] ?? null;
159 $GLOBALS['TSFE'] = new \stdClass();
160 $GLOBALS['TSFE']->cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
161 $GLOBALS['TSFE']->cObjectDepthCounter = 100;
162 }
163
164 /**
165 * Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
166 *
167 * @see simulateFrontendEnvironment()
168 */
169 protected static function resetFrontendEnvironment()
170 {
171 $GLOBALS['TSFE'] = static::$tsfeBackup;
172 }
173 }