098cadeced4e97541af66f262612531bb19101b5
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / ContentObject / FluidTemplateContentObject.php
1 <?php
2 namespace TYPO3\CMS\Frontend\ContentObject;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Extbase\Service\TypoScriptService;
19 use TYPO3\CMS\Fluid\View\StandaloneView;
20
21 /**
22 * Contains FLUIDTEMPLATE class object
23 */
24 class FluidTemplateContentObject extends AbstractContentObject
25 {
26 /**
27 * @var StandaloneView
28 */
29 protected $view = null;
30
31 /**
32 * @var ContentDataProcessor
33 */
34 protected $contentDataProcessor;
35
36 /**
37 * @param ContentObjectRenderer $cObj
38 */
39 public function __construct(ContentObjectRenderer $cObj)
40 {
41 parent::__construct($cObj);
42 $this->contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class);
43 }
44
45 /**
46 * @param ContentDataProcessor $contentDataProcessor
47 */
48 public function setContentDataProcessor($contentDataProcessor)
49 {
50 $this->contentDataProcessor = $contentDataProcessor;
51 }
52
53 /**
54 * Rendering the cObject, FLUIDTEMPLATE
55 *
56 * Configuration properties:
57 * - file string+stdWrap The FLUID template file
58 * - layoutRootPaths array of filepath+stdWrap Root paths to layouts (fallback)
59 * - partialRootPaths array of filepath+stdWrap Root paths to partials (fallback)
60 * - variable array of cObjects, the keys are the variable names in fluid
61 * - dataProcessing array of data processors which are classes to manipulate $data
62 * - extbase.pluginName
63 * - extbase.controllerExtensionName
64 * - extbase.controllerName
65 * - extbase.controllerActionName
66 *
67 * Example:
68 * 10 = FLUIDTEMPLATE
69 * 10.templateName = MyTemplate
70 * 10.templateRootPaths.10 = EXT:site_configuration/Resources/Private/Templates/
71 * 10.partialRootPaths.10 = EXT:site_configuration/Resources/Private/Patials/
72 * 10.layoutRootPaths.10 = EXT:site_configuration/Resources/Private/Layouts/
73 * 10.variables {
74 * mylabel = TEXT
75 * mylabel.value = Label from TypoScript coming
76 * }
77 *
78 * @param array $conf Array of TypoScript properties
79 * @return string The HTML output
80 */
81 public function render($conf = array())
82 {
83 $parentView = $this->view;
84 $this->initializeStandaloneViewInstance();
85
86 if (!is_array($conf)) {
87 $conf = array();
88 }
89
90 $this->setFormat($conf);
91 $this->setTemplate($conf);
92 $this->setLayoutRootPath($conf);
93 $this->setPartialRootPath($conf);
94 $this->setExtbaseVariables($conf);
95 $this->assignSettings($conf);
96 $variables = $this->getContentObjectVariables($conf);
97 $variables = $this->contentDataProcessor->process($this->cObj, $conf, $variables);
98
99 $this->view->assignMultiple($variables);
100
101 $content = $this->renderFluidView();
102 $content = $this->applyStandardWrapToRenderedContent($content, $conf);
103
104 $this->view = $parentView;
105 return $content;
106 }
107
108 /**
109 * Creating standalone view instance must not be done in construct() as
110 * it can lead to a nasty cache issue since content object instances
111 * are not always re-created by the content object rendered for every
112 * usage, but can be re-used. Thus, we need a fresh instance of
113 * StandaloneView every time render() is called.
114 *
115 * @return void
116 */
117 protected function initializeStandaloneViewInstance()
118 {
119 $this->view = GeneralUtility::makeInstance(StandaloneView::class);
120 }
121
122 /**
123 * Set template
124 *
125 * @param array $conf With possibly set file resource
126 * @return void
127 * @throws \InvalidArgumentException
128 */
129 protected function setTemplate(array $conf)
130 {
131 // Fetch the Fluid template by templateName
132 if (!empty($conf['templateName']) && !empty($conf['templateRootPaths.']) && is_array($conf['templateRootPaths.'])) {
133 $templateRootPaths = array();
134 foreach ($conf['templateRootPaths.'] as $key => $path) {
135 if (strpos($key, '.') === false) {
136 $templateRootPaths[$key] = isset($conf['templateRootPaths.'][$key . '.']) ?
137 GeneralUtility::getFileAbsFileName($this->cObj->stdWrap($conf['templateRootPaths.'][$key], $conf['templateRootPaths.'][$key . '.'])) :
138 GeneralUtility::getFileAbsFileName($path);
139 }
140 }
141 $this->view->setTemplateRootPaths($templateRootPaths);
142 $templateName = isset($conf['templateName.']) ? $this->cObj->stdWrap($conf['templateName'], $conf['templateName.']) : $conf['templateName'];
143 $this->view->setTemplate($templateName);
144 // Fetch the Fluid template by template cObject
145 } elseif (!empty($conf['template']) && !empty($conf['template.'])) {
146 $templateSource = $this->cObj->cObjGetSingle($conf['template'], $conf['template.']);
147 $this->view->setTemplateSource($templateSource);
148 // Fetch the Fluid template by file stdWrap
149 } else {
150 $file = isset($conf['file.']) ? $this->cObj->stdWrap($conf['file'], $conf['file.']) : $conf['file'];
151 /** @var $templateService \TYPO3\CMS\Core\TypoScript\TemplateService */
152 $templateService = $GLOBALS['TSFE']->tmpl;
153 $templatePathAndFilename = $templateService->getFileName($file);
154 $this->view->setTemplatePathAndFilename(PATH_site . $templatePathAndFilename);
155 }
156 }
157
158 /**
159 * Set layout root path if given in configuration
160 *
161 * @param array $conf Configuration array
162 * @return void
163 */
164 protected function setLayoutRootPath(array $conf)
165 {
166 // Override the default layout path via typoscript
167 $layoutPaths = array();
168 if (isset($conf['layoutRootPath']) || isset($conf['layoutRootPath.'])) {
169 $layoutRootPath = isset($conf['layoutRootPath.'])
170 ? $this->cObj->stdWrap($conf['layoutRootPath'], $conf['layoutRootPath.'])
171 : $conf['layoutRootPath'];
172 $layoutPaths[] = GeneralUtility::getFileAbsFileName($layoutRootPath);
173 }
174 if (isset($conf['layoutRootPaths.'])) {
175 foreach ($conf['layoutRootPaths.'] as $key => $path) {
176 if (strpos($key, '.') === false) {
177 $layoutPaths[$key] = isset($conf['layoutRootPaths.'][$key . '.']) ?
178 GeneralUtility::getFileAbsFileName($this->cObj->stdWrap($conf['layoutRootPaths.'][$key], $conf['layoutRootPaths.'][$key . '.'])) :
179 GeneralUtility::getFileAbsFileName($path);
180 }
181 }
182 }
183 if (!empty($layoutPaths)) {
184 $this->view->setLayoutRootPaths($layoutPaths);
185 }
186 }
187
188 /**
189 * Set partial root path if given in configuration
190 *
191 * @param array $conf Configuration array
192 * @return void
193 */
194 protected function setPartialRootPath(array $conf)
195 {
196 $partialPaths = array();
197 if (isset($conf['partialRootPath']) || isset($conf['partialRootPath.'])) {
198 $partialRootPath = isset($conf['partialRootPath.'])
199 ? $this->cObj->stdWrap($conf['partialRootPath'], $conf['partialRootPath.'])
200 : $conf['partialRootPath'];
201 $partialPaths[] = GeneralUtility::getFileAbsFileName($partialRootPath);
202 }
203 if (isset($conf['partialRootPaths.'])) {
204 foreach ($conf['partialRootPaths.'] as $key => $path) {
205 if (strpos($key, '.') === false) {
206 $partialPaths[$key] = isset($conf['partialRootPaths.'][$key . '.']) ?
207 GeneralUtility::getFileAbsFileName($this->cObj->stdWrap($conf['partialRootPaths.'][$key], $conf['partialRootPaths.'][$key . '.'])) :
208 GeneralUtility::getFileAbsFileName($path);
209 }
210 }
211 }
212 if (!empty($partialPaths)) {
213 $this->view->setPartialRootPaths($partialPaths);
214 }
215 }
216
217 /**
218 * Set different format if given in configuration
219 *
220 * @param array $conf Configuration array
221 * @return void
222 */
223 protected function setFormat(array $conf)
224 {
225 $format = isset($conf['format.']) ? $this->cObj->stdWrap($conf['format'], $conf['format.']) : $conf['format'];
226 if ($format) {
227 $this->view->setFormat($format);
228 }
229 }
230
231 /**
232 * Set some extbase variables if given
233 *
234 * @param array $conf Configuration array
235 * @return void
236 */
237 protected function setExtbaseVariables(array $conf)
238 {
239 /** @var $request \TYPO3\CMS\Extbase\Mvc\Request */
240 $requestPluginName = isset($conf['extbase.']['pluginName.']) ? $this->cObj->stdWrap($conf['extbase.']['pluginName'], $conf['extbase.']['pluginName.']) : $conf['extbase.']['pluginName'];
241 if ($requestPluginName) {
242 $this->view->getRequest()->setPluginName($requestPluginName);
243 }
244 $requestControllerExtensionName = isset($conf['extbase.']['controllerExtensionName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerExtensionName'], $conf['extbase.']['controllerExtensionName.']) : $conf['extbase.']['controllerExtensionName'];
245 if ($requestControllerExtensionName) {
246 $this->view->getRequest()->setControllerExtensionName($requestControllerExtensionName);
247 }
248 $requestControllerName = isset($conf['extbase.']['controllerName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerName'], $conf['extbase.']['controllerName.']) : $conf['extbase.']['controllerName'];
249 if ($requestControllerName) {
250 $this->view->getRequest()->setControllerName($requestControllerName);
251 }
252 $requestControllerActionName = isset($conf['extbase.']['controllerActionName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerActionName'], $conf['extbase.']['controllerActionName.']) : $conf['extbase.']['controllerActionName'];
253 if ($requestControllerActionName) {
254 $this->view->getRequest()->setControllerActionName($requestControllerActionName);
255 }
256 }
257
258 /**
259 * Compile rendered content objects in variables array ready to assign to the view
260 *
261 * @param array $conf Configuration array
262 * @return array the variables to be assigned
263 * @throws \InvalidArgumentException
264 */
265 protected function getContentObjectVariables(array $conf)
266 {
267 $variables = array();
268 $reservedVariables = array('data', 'current');
269 // Accumulate the variables to be process and loop them through cObjGetSingle
270 $variablesToProcess = (array)$conf['variables.'];
271 foreach ($variablesToProcess as $variableName => $cObjType) {
272 if (is_array($cObjType)) {
273 continue;
274 }
275 if (!in_array($variableName, $reservedVariables)) {
276 $variables[$variableName] = $this->cObj->cObjGetSingle($cObjType, $variablesToProcess[$variableName . '.']);
277 } else {
278 throw new \InvalidArgumentException(
279 'Cannot use reserved name "' . $variableName . '" as variable name in FLUIDTEMPLATE.',
280 1288095720
281 );
282 }
283 }
284 $variables['data'] = $this->cObj->data;
285 $variables['current'] = $this->cObj->data[$this->cObj->currentValKey];
286 return $variables;
287 }
288
289 /**
290 * Set any TypoScript settings to the view. This is similar to a
291 * default MVC action controller in extbase.
292 *
293 * @param array $conf Configuration
294 * @return void
295 */
296 protected function assignSettings(array $conf)
297 {
298 if (isset($conf['settings.'])) {
299 /** @var $typoScriptService TypoScriptService */
300 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
301 $settings = $typoScriptService->convertTypoScriptArrayToPlainArray($conf['settings.']);
302 $this->view->assign('settings', $settings);
303 }
304 }
305
306 /**
307 * Render fluid standalone view
308 *
309 * @return string
310 */
311 protected function renderFluidView()
312 {
313 return $this->view->render();
314 }
315
316 /**
317 * Apply standard wrap to content
318 *
319 * @param string $content Rendered HTML content
320 * @param array $conf Configuration array
321 * @return string Standard wrapped content
322 */
323 protected function applyStandardWrapToRenderedContent($content, array $conf)
324 {
325 if (isset($conf['stdWrap.'])) {
326 $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
327 }
328 return $content;
329 }
330 }