904e31df6c03e1de78de1158a4ea874aeeaa7ba1
[Packages/TYPO3.CMS.git] / typo3 / sysext / tstemplate / Classes / Controller / TypoScriptTemplateInformationModuleFunctionController.php
1 <?php
2 namespace TYPO3\CMS\Tstemplate\Controller;
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\Backend\Form\FormResultCompiler;
18 use TYPO3\CMS\Backend\Form\NodeFactory;
19 use TYPO3\CMS\Backend\Module\AbstractFunctionModule;
20 use TYPO3\CMS\Backend\Utility\BackendUtility;
21 use TYPO3\CMS\Core\DataHandling\DataHandler;
22 use TYPO3\CMS\Core\Imaging\Icon;
23 use TYPO3\CMS\Core\Imaging\IconFactory;
24 use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
25 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Core\Utility\HttpUtility;
28 use TYPO3\CMS\Lang\LanguageService;
29
30 /**
31 * This class displays the Info/Modify screen of the Web > Template module
32 */
33 class TypoScriptTemplateInformationModuleFunctionController extends AbstractFunctionModule
34 {
35 /**
36 * Indicator for t3editor, whether data is stored
37 *
38 * @var bool
39 */
40 public $tce_processed = false;
41
42 /**
43 * @var TypoScriptTemplateModuleController
44 */
45 public $pObj;
46
47 /**
48 * @var IconFactory
49 */
50 protected $iconFactory;
51
52 /**
53 * Constructor
54 */
55 public function __construct()
56 {
57 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
58 }
59
60 /**
61 * Creates a row for a HTML table
62 *
63 * @param string $label The label to be shown (e.g. 'Title:', 'Sitetitle:')
64 * @param string $data The data/information to be shown (e.g. 'Template for my site')
65 * @param string $field The field/variable to be sent on clicking the edit icon (e.g. 'title', 'sitetitle')
66 * @param int $id The field/variable to be sent on clicking the edit icon (e.g. 'title', 'sitetitle')
67 * @return string A row for a HTML table
68 */
69 public function tableRow($label, $data, $field, $id)
70 {
71 $lang = $this->getLanguageService();
72 if ($field === 'config' || $field === 'constants') {
73 $urlParameters = [
74 'id' => $this->pObj->id,
75 'e' => [
76 $field => 1
77 ]
78 ];
79 $url = BackendUtility::getModuleUrl('web_ts', $urlParameters);
80 } else {
81 $urlParameters = [
82 'edit' => [
83 'sys_template' => [
84 $id => 'edit'
85 ]
86 ],
87 'columnsOnly' => $field,
88 'createExtension' => 0,
89 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
90 ];
91 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
92 }
93 $title = $lang->sL('LLL:EXT:lang/locallang_common.xlf:editField');
94 $startAnchor = '<a href="' . htmlspecialchars($url) . '" title="' . htmlspecialchars($title) . '">';
95 $icon = $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render();
96 $ret = '<tr><td>';
97 $ret .= $startAnchor . '<strong>' . $label . '</strong></a>';
98 $ret .= '</td><td width="80%">' . $data . '</td><td>' . $startAnchor . '<span class="btn btn-default">' . $icon . '</span></a></td></tr>';
99 return $ret;
100 }
101
102 /**
103 * Create an instance of \TYPO3\CMS\Core\TypoScript\ExtendedTemplateService in
104 * $GLOBALS['tmpl'] and looks for the first (visible) template
105 * record. If $template_uid was given and greater than zero, this record will be checked.
106 *
107 * Initializes the module. Done in this function because we may need to re-initialize if data is submitted!
108 *
109 * @param int $pageId The uid of the current page
110 * @param int $template_uid: The uid of the template record to be rendered (only if more than one template on the current page)
111 * @return bool Returns TRUE if a template record was found, otherwise FALSE
112 */
113 public function initialize_editor($pageId, $template_uid = 0)
114 {
115 /** @var ExtendedTemplateService $tmpl */
116 $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
117 $GLOBALS['tmpl'] = $tmpl;
118 $tmpl->init();
119
120 // Get the row of the first VISIBLE template of the page. where clause like the frontend.
121 $GLOBALS['tplRow'] = $tmpl->ext_getFirstTemplate($pageId, $template_uid);
122 if (is_array($GLOBALS['tplRow'])) {
123 $GLOBALS['tplRow'] = $this->processTemplateRowAfterLoading($GLOBALS['tplRow']);
124 return true;
125 }
126 return false;
127 }
128
129 /**
130 * Process template row after loading
131 *
132 * @param array $tplRow Template row
133 * @return array Preprocessed template row
134 */
135 public function processTemplateRowAfterLoading(array $tplRow)
136 {
137 if ($this->pObj->MOD_SETTINGS['includeTypoScriptFileContent']) {
138 // Let the recursion detection counter start at 91, so that only 10 recursive calls will be resolved
139 // Otherwise the editor will be bloated with way to many lines making it hard the break the cyclic recursion.
140 $tplRow['config'] = TypoScriptParser::checkIncludeLines($tplRow['config'], 91);
141 $tplRow['constants'] = TypoScriptParser::checkIncludeLines($tplRow['constants'], 91);
142 }
143 return $tplRow;
144 }
145
146 /**
147 * Process template row before saving
148 *
149 * @param array $tplRow Template row
150 * @return array Preprocessed template row
151 */
152 public function processTemplateRowBeforeSaving(array $tplRow)
153 {
154 if ($this->pObj->MOD_SETTINGS['includeTypoScriptFileContent']) {
155 $tplRow = TypoScriptParser::extractIncludes_array($tplRow);
156 }
157 return $tplRow;
158 }
159
160 /**
161 * @return LanguageService
162 */
163 protected function getLanguageService()
164 {
165 return $GLOBALS['LANG'];
166 }
167
168 /**
169 * The main processing method if this class
170 *
171 * @return string Information of the template status or the taken actions as HTML string
172 */
173 public function main()
174 {
175 $lang = $this->getLanguageService();
176 $lang->includeLLFile('EXT:tstemplate/Resources/Private/Language/locallang_info.xlf');
177 $this->pObj->MOD_MENU['includeTypoScriptFileContent'] = true;
178 $e = $this->pObj->e;
179 // Checking for more than one template an if, set a menu...
180 $manyTemplatesMenu = $this->pObj->templateMenu();
181 $template_uid = 0;
182 if ($manyTemplatesMenu) {
183 $template_uid = $this->pObj->MOD_SETTINGS['templatesOnPage'];
184 }
185 // Initialize
186 $existTemplate = $this->initialize_editor($this->pObj->id, $template_uid);
187 $tplRow = $GLOBALS['tplRow'];
188 $saveId = 0;
189 if ($existTemplate) {
190 $saveId = $tplRow['_ORIG_uid'] ? $tplRow['_ORIG_uid'] : $tplRow['uid'];
191 }
192 // Create extension template
193 $newId = $this->pObj->createTemplate($this->pObj->id, $saveId);
194 if ($newId) {
195 // Switch to new template
196 $urlParameters = [
197 'id' => $this->pObj->id,
198 'SET[templatesOnPage]' => $newId
199 ];
200 $url = BackendUtility::getModuleUrl('web_ts', $urlParameters);
201 HttpUtility::redirect($url);
202 }
203 $tce = null;
204 $theOutput = '';
205 if ($existTemplate) {
206 // Update template ?
207 $POST = GeneralUtility::_POST();
208 if (
209 isset($POST['_savedok'])
210 || isset($POST['_saveandclosedok'])
211 ) {
212 // Set the data to be saved
213 $recData = [];
214 $alternativeFileName = [];
215 if (is_array($POST['data'])) {
216 $recData = $POST['data'];
217 }
218 if (!empty($recData)) {
219 $recData['sys_template'][$saveId] = $this->processTemplateRowBeforeSaving($recData['sys_template'][$saveId]);
220 // Create new tce-object
221 $tce = GeneralUtility::makeInstance(DataHandler::class);
222 $tce->alternativeFileName = $alternativeFileName;
223 // Initialize
224 $tce->start($recData, []);
225 // Saved the stuff
226 $tce->process_datamap();
227 // Clear the cache (note: currently only admin-users can clear the cache in tce_main.php)
228 $tce->clear_cacheCmd('all');
229 // tce were processed successfully
230 $this->tce_processed = true;
231 // re-read the template ...
232 $this->initialize_editor($this->pObj->id, $template_uid);
233 $tplRow = $GLOBALS['tplRow'];
234 // reload template menu
235 $manyTemplatesMenu = $this->pObj->templateMenu();
236 }
237 }
238 // Hook post updating template/TCE processing
239 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['postTCEProcessingHook'])) {
240 $postTCEProcessingHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['postTCEProcessingHook'];
241 if (is_array($postTCEProcessingHook)) {
242 $hookParameters = [
243 'POST' => $POST,
244 'tce' => $tce
245 ];
246 foreach ($postTCEProcessingHook as $hookFunction) {
247 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
248 }
249 }
250 }
251 $content = '<a href="#" class="t3-js-clickmenutrigger" data-table="sys_template" data-uid="' . $tplRow['uid'] . '" data-listframe="1">' . $this->iconFactory->getIconForRecord('sys_template', $tplRow, Icon::SIZE_SMALL)->render() . '</a><strong>&nbsp;' . htmlspecialchars($tplRow['title']) . '</strong>' . (trim($tplRow['sitetitle']) ? htmlspecialchars(' (' . $tplRow['sitetitle'] . ')') : '');
252 $theOutput .= '<h3>' . htmlspecialchars($lang->getLL('templateInformation')) . '</h3><div>' . $content . '</div>';
253 if ($manyTemplatesMenu) {
254 $theOutput .= '<div>' . $manyTemplatesMenu . '</div>';
255 }
256 $numberOfRows = 35;
257 // If abort pressed, nothing should be edited:
258 if (isset($POST['_saveandclosedok'])) {
259 unset($e);
260 }
261 $textareaFieldConfig = [
262 'config' => [
263 'type' => 'text',
264 'cols' => 48,
265 'wrap' => 'OFF',
266 'softref' => 'email[subst],url[subst]'
267 ],
268 'defaultExtras' => 'fixed-font: enable-tab'
269 ];
270 $formData = [
271 'databaseRow' => [
272 'uid' => $tplRow['uid'],
273 'pageId' => $this->pObj->id
274 ],
275 'tableName' => 'sys_template',
276 'processedTca' => [
277 'columns' => [
278 'pageId' => [
279 'config' => [
280 'type' => 'passthrough'
281 ]
282 ]
283 ],
284 'types' => [
285 1 => [
286 'showitem' => '',
287 ],
288 ],
289 ],
290 'recordTypeValue' => 1,
291 'inlineStructure' => [],
292 'renderType' => 'fullRecordContainer'
293 ];
294 if (isset($e['constants'])) {
295 $formData['databaseRow']['constants'] = $tplRow['constants'];
296 $formData['processedTca']['columns']['constants'] = array_merge_recursive(
297 $textareaFieldConfig,
298 [
299 'label' => $lang->getLL('constants'),
300 'config' => [
301 'rows' => min($numberOfRows, substr_count($tplRow['constants'], LF))
302 ]
303 ]
304 );
305 }
306 if (isset($e['config'])) {
307 $formData['databaseRow']['config'] = $tplRow['config'];
308 $formData['processedTca']['columns']['config'] = array_merge_recursive(
309 $textareaFieldConfig,
310 [
311 'label' => $lang->getLL('setup'),
312 'config' => [
313 'rows' => min($numberOfRows, substr_count($tplRow['config'], LF))
314 ]
315 ]
316 );
317 }
318
319 // Hook before compiling the output
320 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['preOutputProcessingHook'])) {
321 $preOutputProcessingHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['preOutputProcessingHook'];
322 if (is_array($preOutputProcessingHook)) {
323 $hookParameters = [
324 'formData' => &$formData
325 ];
326 foreach ($preOutputProcessingHook as $hookFunction) {
327 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
328 }
329 }
330 }
331
332 if (!empty($formData['processedTca']['columns'])) {
333 $formData['processedTca']['types'][1]['showitem'] = implode(',', array_keys($formData['processedTca']['columns']));
334
335 $resultArray = GeneralUtility::makeInstance(NodeFactory::class)->create($formData)->render();
336 $formResultCompiler = GeneralUtility::makeInstance(FormResultCompiler::class);
337 $formResultCompiler->mergeResult($resultArray);
338
339 $theOutput .= $formResultCompiler->JStop()
340 . $formResultCompiler->printNeededJSFunctions()
341 . $resultArray['html'];
342 $theOutput .= '<div class="checkbox"><label for="checkIncludeTypoScriptFileContent">' . BackendUtility::getFuncCheck($this->pObj->id,
343 'SET[includeTypoScriptFileContent]', $this->pObj->MOD_SETTINGS['includeTypoScriptFileContent'],
344 '', GeneralUtility::implodeArrayForUrl('', ['e' => $this->pObj->e]), 'id="checkIncludeTypoScriptFileContent"');
345 $theOutput .= $lang->getLL('includeTypoScriptFileContent') . '</label></div><br />';
346 }
347
348 // Processing:
349 $outCode = '';
350 $outCode .= $this->tableRow($lang->getLL('title'), htmlspecialchars($tplRow['title']), 'title', $tplRow['uid']);
351 $outCode .= $this->tableRow($lang->getLL('sitetitle'), htmlspecialchars($tplRow['sitetitle']), 'sitetitle', $tplRow['uid']);
352 $outCode .= $this->tableRow($lang->getLL('description'), nl2br(htmlspecialchars($tplRow['description'])), 'description', $tplRow['uid']);
353 $outCode .= $this->tableRow($lang->getLL('constants'), sprintf($lang->getLL('editToView'), trim($tplRow['constants']) ? count(explode(LF, $tplRow['constants'])) : 0), 'constants', $tplRow['uid']);
354 $outCode .= $this->tableRow($lang->getLL('setup'), sprintf($lang->getLL('editToView'), trim($tplRow['config']) ? count(explode(LF, $tplRow['config'])) : 0), 'config', $tplRow['uid']);
355 $outCode = '<div class="table-fit"><table class="table table-striped table-hover">' . $outCode . '</table></div>';
356
357 // Edit all icon:
358 $urlParameters = [
359 'edit' => [
360 'sys_template' => [
361 $tplRow['uid'] => 'edit'
362 ]
363 ],
364 'createExtension' => 0,
365 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
366 ];
367 $url = BackendUtility::getModuleUrl('record_edit', $urlParameters);
368 $title = htmlspecialchars($lang->getLL('editTemplateRecord'));
369 $icon = $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render();
370 $outCode .= '<br /><a class="btn btn-default" href="' . htmlspecialchars($url)
371 . '"><strong>' . $icon . '&nbsp;' . $title . '</strong></a>';
372 $theOutput .= '<div>' . $outCode . '</div>';
373
374 // hook after compiling the output
375 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['postOutputProcessingHook'])) {
376 $postOutputProcessingHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/tstemplate_info/class.tx_tstemplateinfo.php']['postOutputProcessingHook'];
377 if (is_array($postOutputProcessingHook)) {
378 $hookParameters = [
379 'theOutput' => &$theOutput,
380 'POST' => $POST,
381 'e' => $e,
382 'tplRow' => $tplRow,
383 'numberOfRows' => $numberOfRows
384 ];
385 foreach ($postOutputProcessingHook as $hookFunction) {
386 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
387 }
388 }
389 }
390 } else {
391 $theOutput .= $this->pObj->noTemplate(1);
392 }
393 return $theOutput;
394 }
395 }