[TASK] Protect info extension
[Packages/TYPO3.CMS.git] / typo3 / sysext / info / Classes / Controller / InfoPageTyposcriptConfigController.php
1 <?php
2 namespace TYPO3\CMS\Info\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\Routing\UriBuilder;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
20 use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
21 use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
22 use TYPO3\CMS\Core\Database\ConnectionPool;
23 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
24 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
25 use TYPO3\CMS\Core\Imaging\Icon;
26 use TYPO3\CMS\Core\Imaging\IconFactory;
27 use TYPO3\CMS\Core\Localization\LanguageService;
28 use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
29 use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser;
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Core\Utility\MathUtility;
32 use TYPO3\CMS\Fluid\View\StandaloneView;
33
34 /**
35 * Page TSconfig viewer in Web -> Info
36 */
37 class InfoPageTyposcriptConfigController
38 {
39 use PublicPropertyDeprecationTrait;
40 use PublicMethodDeprecationTrait;
41
42 /**
43 * @var array
44 */
45 private $deprecatedPublicProperties = [
46 'pObj' => 'Using InfoPageTyposcriptConfigController::$pObj is deprecated and will not be possible anymore in TYPO3 v10.',
47 'function_key' => 'Using InfoPageTyposcriptConfigController::$function_key is deprecated, property will be removed in TYPO3 v10.',
48 'extClassConf' => 'Using InfoPageTyposcriptConfigController::$extClassConf is deprecated, property will be removed in TYPO3 v10.',
49 'localLangFile' => 'Using InfoPageTyposcriptConfigController::$localLangFile is deprecated, property will be removed in TYPO3 v10.',
50 'extObj' => 'Using InfoPageTyposcriptConfigController::$extObj is deprecated, property will be removed in TYPO3 v10.',
51 ];
52
53 /**
54 * @var array
55 */
56 private $deprecatedPublicMethods = [
57 'modMenu' => 'Using InfoPageTyposcriptConfigController::modMenu() is deprecated and will not be possible anymore in TYPO3 v10.',
58 'extObjContent' => 'Using InfoPageTyposcriptConfigController::extObjContent() is deprecated, method will be removed in TYPO3 v10.',
59 ];
60
61 /**
62 * @var IconFactory
63 */
64 protected $iconFactory;
65
66 /**
67 * @var StandaloneView
68 */
69 protected $view;
70
71 /**
72 * @var InfoModuleController Contains a reference to the parent calling object
73 */
74 protected $pObj;
75
76 /**
77 * @var int Value of the GET/POST var 'id'
78 */
79 protected $id;
80
81 /**
82 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
83 */
84 protected $extObj;
85
86 /**
87 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
88 */
89 protected $localLangFile = '';
90
91 /**
92 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
93 */
94 protected $extClassConf;
95
96 /**
97 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
98 */
99 protected $function_key = '';
100
101 /**
102 * Init, called from parent object
103 *
104 * @param InfoModuleController $pObj A reference to the parent (calling) object
105 */
106 public function init($pObj)
107 {
108 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
109 $languageService = $this->getLanguageService();
110 $languageService->includeLLFile('EXT:info/Resources/Private/Language/InfoPageTsConfig.xlf');
111 $this->view = $this->getFluidTemplateObject();
112 $this->pObj = $pObj;
113 // Local lang:
114 if (!empty($this->localLangFile)) {
115 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
116 $languageService->includeLLFile($this->localLangFile);
117 }
118 $this->id = (int)GeneralUtility::_GP('id');
119 // Setting MOD_MENU items as we need them for logging:
120 $this->pObj->MOD_MENU = array_merge($this->pObj->MOD_MENU, $this->modMenu());
121 }
122
123 /**
124 * Main, called from parent object
125 *
126 * @return string HTML output
127 */
128 public function main()
129 {
130 $pageId = (int)GeneralUtility::_GP('id');
131 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
132
133 if ($pageId === 0) {
134 $this->view->assign('pageZero', 1);
135 $pagesUsingTSConfig = $this->getOverviewOfPagesUsingTSConfig();
136 if (count($pagesUsingTSConfig) > 0) {
137 $this->view->assign('overviewOfPagesUsingTSConfig', $pagesUsingTSConfig);
138 }
139 } else {
140 if ($this->pObj->MOD_SETTINGS['tsconf_parts'] == 99) {
141 $TSparts = BackendUtility::getRawPagesTSconfig($this->id);
142 $lines = [];
143 $pUids = [];
144
145 foreach ($TSparts as $k => $v) {
146 if ($k !== 'uid_0') {
147 $line = [];
148 if ($k === 'defaultPageTSconfig') {
149 $line['defaultPageTSconfig'] = 1;
150 } else {
151 $editIdList = substr($k, 4);
152 $pUids[] = $editIdList;
153 $row = BackendUtility::getRecordWSOL('pages', $editIdList);
154
155 $icon = $this->iconFactory->getIconForRecord('pages', $row, Icon::SIZE_SMALL);
156 $urlParameters = [
157 'edit' => [
158 'pages' => [
159 $editIdList => 'edit',
160 ]
161 ],
162 'columnsOnly' => 'TSconfig',
163 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
164 ];
165 $line['editIcon'] = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
166 $line['editTitle'] = 'editTSconfig';
167 $line['title'] = BackendUtility::wrapClickMenuOnIcon($icon, 'pages', $row['uid'])
168 . ' ' . htmlspecialchars(BackendUtility::getRecordTitle('pages', $row));
169 }
170 $tsparser = GeneralUtility::makeInstance(TypoScriptParser::class);
171 $tsparser->lineNumberOffset = 0;
172 $line['content'] = $tsparser->doSyntaxHighlight(trim($v) . LF);
173 $lines[] = $line;
174 }
175 }
176
177 if (!empty($pUids)) {
178 $urlParameters = [
179 'edit' => [
180 'pages' => [
181 implode(',', $pUids) => 'edit',
182 ]
183 ],
184 'columnsOnly' => 'TSconfig',
185 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
186 ];
187 $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
188 $editIcon = htmlspecialchars($url);
189 $editTitle = 'editTSconfig_all';
190 } else {
191 $editIcon = '';
192 $editTitle = '';
193 }
194
195 $this->view->assign('tsconfParts99', 1);
196 $this->view->assign('csh', BackendUtility::cshItem('_MOD_web_info', 'tsconfig_edit', null, '|'));
197 $this->view->assign('lines', $lines);
198 $this->view->assign('editIcon', $editIcon);
199 $this->view->assign('editTitle', $editTitle);
200 } else {
201 $this->view->assign('tsconfParts99', 0);
202 // Defined global here!
203 $tmpl = GeneralUtility::makeInstance(ExtendedTemplateService::class);
204 $tmpl->ext_expandAllNotes = 1;
205 $tmpl->ext_noPMicons = 1;
206
207 $pageTsConfig = BackendUtility::getPagesTSconfig($this->id);
208 switch ($this->pObj->MOD_SETTINGS['tsconf_parts']) {
209 case '1':
210 $pageTsConfig = $pageTsConfig['mod.'] ?? [];
211 break;
212 case '1a':
213 $pageTsConfig = $pageTsConfig['mod.']['web_layout.'] ?? [];
214 break;
215 case '1b':
216 $pageTsConfig = $pageTsConfig['mod.']['web_view.'] ?? [];
217 break;
218 case '1c':
219 $pageTsConfig = $pageTsConfig['mod.']['web_modules.'] ?? [];
220 break;
221 case '1d':
222 $pageTsConfig = $pageTsConfig['mod.']['web_list.'] ?? [];
223 break;
224 case '1e':
225 $pageTsConfig = $pageTsConfig['mod.']['web_info.'] ?? [];
226 break;
227 case '1f':
228 $pageTsConfig = $pageTsConfig['mod.']['web_func.'] ?? [];
229 break;
230 case '1g':
231 $pageTsConfig = $pageTsConfig['mod.']['web_ts.'] ?? [];
232 break;
233 case '2':
234 $pageTsConfig = $pageTsConfig['RTE.'] ?? [];
235 break;
236 case '5':
237 $pageTsConfig = $pageTsConfig['TCEFORM.'] ?? [];
238 break;
239 case '6':
240 $pageTsConfig = $pageTsConfig['TCEMAIN.'] ?? [];
241 break;
242 case '3':
243 $pageTsConfig = $pageTsConfig['TSFE.'] ?? [];
244 break;
245 case '4':
246 $pageTsConfig = $pageTsConfig['user.'] ?? [];
247 break;
248 default:
249 // Entire array
250 }
251
252 $this->view->assign('csh', BackendUtility::cshItem('_MOD_web_info', 'tsconfig_hierarchy', null, '|'));
253 $this->view->assign('tree', $tmpl->ext_getObjTree($pageTsConfig, '', '', '', '', $this->pObj->MOD_SETTINGS['tsconf_alphaSort']));
254 }
255 $this->view->assign('alphaSort', BackendUtility::getFuncCheck($this->id, 'SET[tsconf_alphaSort]', $this->pObj->MOD_SETTINGS['tsconf_alphaSort'], '', '', 'id="checkTsconf_alphaSort"'));
256 $this->view->assign('dropdownMenu', BackendUtility::getDropdownMenu($this->id, 'SET[tsconf_parts]', $this->pObj->MOD_SETTINGS['tsconf_parts'], $this->pObj->MOD_MENU['tsconf_parts']));
257 }
258 return $this->view->render();
259 }
260
261 /**
262 * Function menu initialization
263 *
264 * @return array Menu array
265 */
266 protected function modMenu()
267 {
268 $lang = $this->getLanguageService();
269 $modMenuAdd = [
270 'tsconf_parts' => [
271 0 => $lang->getLL('tsconf_parts_0'),
272 1 => $lang->getLL('tsconf_parts_1'),
273 '1a' => $lang->getLL('tsconf_parts_1a'),
274 '1b' => $lang->getLL('tsconf_parts_1b'),
275 '1c' => $lang->getLL('tsconf_parts_1c'),
276 '1d' => $lang->getLL('tsconf_parts_1d'),
277 '1e' => $lang->getLL('tsconf_parts_1e'),
278 '1f' => $lang->getLL('tsconf_parts_1f'),
279 '1g' => $lang->getLL('tsconf_parts_1g'),
280 2 => 'RTE.',
281 5 => 'TCEFORM.',
282 6 => 'TCEMAIN.',
283 3 => 'TSFE.',
284 4 => 'user.',
285 99 => $lang->getLL('tsconf_configFields')
286 ],
287 'tsconf_alphaSort' => '1'
288 ];
289 if (!$this->getBackendUser()->isAdmin()) {
290 unset($modMenuAdd['tsconf_parts'][99]);
291 }
292 return $modMenuAdd;
293 }
294
295 /**
296 * Renders table rows of all pages containing TSConfig together with its rootline
297 *
298 * @return array
299 */
300 protected function getOverviewOfPagesUsingTSConfig()
301 {
302 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
303 $queryBuilder->getRestrictions()
304 ->removeAll()
305 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
306 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
307
308 $res = $queryBuilder
309 ->select('uid', 'TSconfig')
310 ->from('pages')
311 ->where(
312 $queryBuilder->expr()->neq(
313 'TSconfig',
314 $queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
315 )
316 )
317 ->groupBy('uid')
318 ->execute();
319
320 $pageArray = [];
321
322 while ($row = $res->fetch()) {
323 $this->setInPageArray($pageArray, BackendUtility::BEgetRootLine($row['uid'], 'AND 1=1'), $row);
324 }
325 return $this->getList($pageArray);
326 }
327
328 /**
329 * Set page in array
330 * This function is called recursively and builds a multi-dimensional array that reflects the page
331 * hierarchy.
332 *
333 * @param array $hierarchicArray The hierarchic array (passed by reference)
334 * @param array $rootlineArray The rootline array
335 * @param array $row The row from the database containing the uid and TSConfig fields
336 */
337 protected function setInPageArray(&$hierarchicArray, $rootlineArray, $row)
338 {
339 ksort($rootlineArray);
340 reset($rootlineArray);
341 if (!$rootlineArray[0]['uid']) {
342 array_shift($rootlineArray);
343 }
344 $currentElement = current($rootlineArray);
345 $hierarchicArray[$currentElement['uid']] = htmlspecialchars($currentElement['title']);
346 array_shift($rootlineArray);
347 if (!empty($rootlineArray)) {
348 if (!isset($hierarchicArray[$currentElement['uid'] . '.'])) {
349 $hierarchicArray[$currentElement['uid'] . '.'] = [];
350 }
351 $this->setInPageArray($hierarchicArray[$currentElement['uid'] . '.'], $rootlineArray, $row);
352 } else {
353 $hierarchicArray[$currentElement['uid'] . '_'] = $this->extractLinesFromTSConfig($row);
354 }
355 }
356
357 /**
358 * Extract the lines of TSConfig from a given pages row
359 *
360 * @param array $row The row from the database containing the uid and TSConfig fields
361 * @return array
362 */
363 protected function extractLinesFromTSConfig(array $row)
364 {
365 $out = [];
366 $includeLines = 0;
367 $out['uid'] = $row['uid'];
368 $lines = GeneralUtility::trimExplode("\r\n", $row['TSconfig']);
369 foreach ($lines as $line) {
370 if (strpos($line, '<INCLUDE_TYPOSCRIPT:') !== false) {
371 $includeLines++;
372 }
373 }
374 $out['includeLines'] = $includeLines;
375 $out['writtenLines'] = (count($lines) - $includeLines);
376 return $out;
377 }
378
379 /**
380 * Get the list of pages to show.
381 * This function is called recursively
382 *
383 * @param array $pageArray The Page Array
384 * @param array $lines Lines that have been processed up to this point
385 * @param int $pageDepth The level of the current $pageArray being processed
386 * @return array
387 */
388 protected function getList($pageArray, $lines = [], $pageDepth = 0)
389 {
390 if (!is_array($pageArray)) {
391 return $lines;
392 }
393
394 foreach ($pageArray as $identifier => $_) {
395 if (!MathUtility::canBeInterpretedAsInteger($identifier)) {
396 continue;
397 }
398 $line = [];
399 $line['padding'] = ($pageDepth * 20) + 10;
400 if (isset($pageArray[$identifier . '_'])) {
401 $line['link'] = GeneralUtility::linkThisScript(['id' => $identifier]);
402 $line['icon'] = $this->iconFactory->getIconForRecord('pages', BackendUtility::getRecordWSOL('pages', $identifier), Icon::SIZE_SMALL)->render();
403 $line['title'] = 'ID: ' . $identifier;
404 $line['pageTitle'] = GeneralUtility::fixed_lgd_cs($pageArray[$identifier], 30);
405 $line['includedFiles'] = ($pageArray[$identifier . '_']['includeLines'] === 0 ? '' : $pageArray[$identifier . '_']['includeLines']);
406 $line['lines'] = ($pageArray[$identifier . '_']['writtenLines'] === 0 ? '' : $pageArray[$identifier . '_']['writtenLines']);
407 } else {
408 $line['link'] = '';
409 $line['icon'] = $this->iconFactory->getIconForRecord('pages', BackendUtility::getRecordWSOL('pages', $identifier), Icon::SIZE_SMALL)->render();
410 $line['title'] = '';
411 $line['pageTitle'] = GeneralUtility::fixed_lgd_cs($pageArray[$identifier], 30);
412 $line['includedFiles'] = '';
413 $line['lines'] = '';
414 }
415 $lines[] = $line;
416 $lines = $this->getList($pageArray[$identifier . '.'], $lines, $pageDepth + 1);
417 }
418 return $lines;
419 }
420
421 /**
422 * returns a new standalone view, shorthand function
423 *
424 * @return StandaloneView
425 */
426 protected function getFluidTemplateObject()
427 {
428 $view = GeneralUtility::makeInstance(StandaloneView::class);
429 $view->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Layouts')]);
430 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Partials')]);
431 $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Templates')]);
432
433 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:info/Resources/Private/Templates/PageTsConfig.html'));
434
435 $view->getRequest()->setControllerExtensionName('info');
436 return $view;
437 }
438
439 /**
440 * Called from InfoModuleController until deprecation removal in v10
441 *
442 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
443 */
444 public function checkExtObj()
445 {
446 if (is_array($this->extClassConf) && $this->extClassConf['name']) {
447 $this->extObj = GeneralUtility::makeInstance($this->extClassConf['name']);
448 $this->extObj->init($this->pObj, $this->extClassConf);
449 // Re-write:
450 $this->pObj->MOD_SETTINGS = BackendUtility::getModuleData($this->pObj->MOD_MENU, GeneralUtility::_GP('SET'), 'web_info');
451 }
452 }
453
454 /**
455 * Calls the main function inside ANOTHER sub-submodule which might exist.
456 *
457 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
458 */
459 protected function extObjContent()
460 {
461 if (is_object($this->extObj)) {
462 return $this->extObj->main();
463 }
464 }
465
466 /**
467 * @return BackendUserAuthentication
468 */
469 protected function getBackendUser(): BackendUserAuthentication
470 {
471 return $GLOBALS['BE_USER'];
472 }
473
474 /**
475 * @return LanguageService
476 */
477 protected function getLanguageService(): LanguageService
478 {
479 return $GLOBALS['LANG'];
480 }
481 }