[TASK] Use BE Routing / PSR-7 instead of BackendUtility::getModuleUrl
[Packages/TYPO3.CMS.git] / typo3 / sysext / tstemplate / Classes / Controller / TypoScriptTemplateModuleController.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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Module\BaseScriptClass;
20 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
21 use TYPO3\CMS\Backend\Template\ModuleTemplate;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Database\ConnectionPool;
24 use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
25 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
26 use TYPO3\CMS\Core\DataHandling\DataHandler;
27 use TYPO3\CMS\Core\Imaging\Icon;
28 use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
29 use TYPO3\CMS\Core\Utility\GeneralUtility;
30 use TYPO3\CMS\Core\Utility\MathUtility;
31 use TYPO3\CMS\Fluid\View\StandaloneView;
32 use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
33
34 /**
35 * Module: TypoScript Tools
36 */
37 class TypoScriptTemplateModuleController extends BaseScriptClass
38 {
39 /**
40 * @var string
41 */
42 public $perms_clause;
43
44 /**
45 * @var string
46 */
47 public $sObj;
48
49 /**
50 * @var string
51 */
52 public $edit;
53
54 /**
55 * @var string
56 */
57 public $textExtensions = 'html,htm,txt,css,tmpl,inc,js';
58
59 /**
60 * @var string
61 */
62 public $modMenu_type = '';
63
64 /**
65 * @var string
66 */
67 public $modMenu_dontValidateList = '';
68
69 /**
70 * @var string
71 */
72 public $modMenu_setDefaultList = '';
73
74 /**
75 * @var array
76 */
77 public $pageinfo = [];
78
79 /**
80 * @var bool
81 */
82 public $access = false;
83
84 /**
85 * The name of the module
86 *
87 * @var string
88 */
89 protected $moduleName = 'web_ts';
90
91 /**
92 * ModuleTemplate Container
93 *
94 * @var ModuleTemplate
95 */
96 protected $moduleTemplate;
97
98 /**
99 * @var ExtendedTemplateService
100 */
101 protected $templateService;
102
103 /**
104 * Constructor
105 */
106 public function __construct()
107 {
108 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
109 $this->getLanguageService()->includeLLFile('EXT:tstemplate/Resources/Private/Language/locallang.xlf');
110
111 $this->MCONF = [
112 'name' => $this->moduleName
113 ];
114 $this->moduleTemplate->addJavaScriptCode(
115 'jumpToUrl',
116 '
117 function jumpToUrl(URL) {
118 window.location.href = URL;
119 return false;
120 }
121 '
122 );
123 }
124
125 /**
126 * Init
127 */
128 public function init()
129 {
130 parent::init();
131 $this->id = (int)GeneralUtility::_GP('id');
132 $this->sObj = GeneralUtility::_GP('sObj');
133 $this->edit = GeneralUtility::_GP('edit');
134 $this->perms_clause = $this->getBackendUser()->getPagePermsClause(1);
135 }
136
137 /**
138 * Clear cache
139 */
140 public function clearCache()
141 {
142 if (GeneralUtility::_GP('clear_all_cache')) {
143 /** @var DataHandler $tce */
144 $tce = GeneralUtility::makeInstance(DataHandler::class);
145 $tce->start([], []);
146 $tce->clear_cacheCmd('all');
147 }
148 }
149
150 /**
151 * Main
152 */
153 public function main()
154 {
155 // Access check...
156 // The page will show only if there is a valid page and if this page may be viewed by the user
157 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
158 $this->access = is_array($this->pageinfo);
159 $view = $this->getFluidTemplateObject('tstemplate');
160 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
161 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
162 if ($this->id && $this->access) {
163 $urlParameters = [
164 'id' => $this->id,
165 'template' => 'all'
166 ];
167 $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
168
169 // JavaScript
170 $this->moduleTemplate->addJavaScriptCode(
171 'TSTemplateInlineJS',
172 'function uFormUrl(aname) {
173 document.forms[0].action = ' . GeneralUtility::quoteJSvalue($aHref . '#') . '+aname;
174 }
175 function brPoint(lnumber,t) {
176 window.location.href = '
177 . GeneralUtility::quoteJSvalue(
178 $aHref . '&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\'
179 . 'TypoScriptTemplateObjectBrowserModuleFunctionController&SET[ts_browser_type]='
180 ) . '+(t?"setup":"const")+"&breakPointLN="+lnumber;
181 return false;
182 }
183 if (top.fsMod) top.fsMod.recentIds["web"] = ' . $this->id . ';'
184 );
185 $this->moduleTemplate->getPageRenderer()->addCssInlineBlock(
186 'TSTemplateInlineStyle',
187 'TABLE#typo3-objectBrowser { width: 100%; margin-bottom: 24px; }
188 TABLE#typo3-objectBrowser A { text-decoration: none; }
189 TABLE#typo3-objectBrowser .comment { color: maroon; font-weight: bold; }
190 .ts-typoscript { width: 100%; }
191 .tsob-search-submit {margin-left: 3px; margin-right: 3px;}
192 .tst-analyzer-options { margin:5px 0; }'
193 );
194 // Setting up the context sensitive menu:
195 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
196 // Build the module content
197 $view->assign('actionName', $aHref);
198 $view->assign('typoscriptTemplateModuleContent', $this->getExtObjContent());
199 // Setting up the buttons and markers for docheader
200 $this->getButtons();
201 $this->generateMenu();
202 } else {
203 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
204 $queryBuilder->getRestrictions()
205 ->removeAll()
206 ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
207 ->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
208
209 $result = $queryBuilder->select('pages.uid')
210 ->addSelectLiteral(
211 $queryBuilder->expr()->count('*', 'count'),
212 $queryBuilder->expr()->max('sys_template.root', 'root_max_val'),
213 $queryBuilder->expr()->min('sys_template.root', 'root_min_val')
214 )
215 ->from('pages')
216 ->from('sys_template')
217 ->where($queryBuilder->expr()->eq('pages.uid', $queryBuilder->quoteIdentifier('sys_template.pid')))
218 ->groupBy('pages.uid')
219 ->orderBy('pages.pid')
220 ->addOrderBy('pages.sorting')
221 ->execute();
222
223 $pArray = [];
224 while ($record = $result->fetch()) {
225 $this->setInPageArray($pArray, BackendUtility::BEgetRootLine($record['uid'], 'AND 1=1'), $record);
226 }
227
228 $view->getRenderingContext()->setControllerAction('PageZero');
229 $view->assign('templateList', $this->renderList($pArray));
230
231 // RENDER LIST of pages with templates, END
232 // Setting up the buttons and markers for docheader
233 $this->getButtons();
234 }
235 $this->content = $view->render();
236 }
237
238 /**
239 * Generates the menu based on $this->MOD_MENU
240 *
241 * @throws \InvalidArgumentException
242 */
243 protected function generateMenu()
244 {
245 $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
246 $menu->setIdentifier('WebFuncJumpMenu');
247 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
248 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
249 foreach ($this->MOD_MENU['function'] as $controller => $title) {
250 $item = $menu
251 ->makeMenuItem()
252 ->setHref(
253 (string)$uriBuilder->buildUriFromRoute(
254 $this->moduleName,
255 [
256 'id' => $this->id,
257 'SET' => [
258 'function' => $controller
259 ]
260 ]
261 )
262 )
263 ->setTitle($title);
264 if ($controller === $this->MOD_SETTINGS['function']) {
265 $item->setActive(true);
266 }
267 $menu->addMenuItem($item);
268 }
269 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
270 }
271
272 /**
273 * Injects the request object for the current request or subrequest
274 * Then checks for module functions that have hooked in, and renders menu etc.
275 *
276 * @param ServerRequestInterface $request the current request
277 * @param ResponseInterface $response
278 * @return ResponseInterface the response with the content
279 */
280 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
281 {
282 $GLOBALS['SOBE'] = $this;
283 $this->init();
284
285 // Checking for first level external objects
286 $this->checkExtObj();
287
288 $this->clearCache();
289 $this->main();
290
291 $this->moduleTemplate->setContent($this->content);
292 $response->getBody()->write($this->moduleTemplate->renderContent());
293 return $response;
294 }
295
296 /**
297 * Create the panel of buttons for submitting the form or otherwise perform operations.
298 */
299 protected function getButtons()
300 {
301 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
302 $lang = $this->getLanguageService();
303
304 if ($this->id && $this->access) {
305 // View page
306 $viewButton = $buttonBar->makeLinkButton()
307 ->setHref('#')
308 ->setOnClick(BackendUtility::viewOnClick(
309 $this->pageinfo['uid'],
310 '',
311 BackendUtility::BEgetRootLine($this->pageinfo['uid'])
312 ))
313 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
314 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-page', Icon::SIZE_SMALL));
315 $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 99);
316
317 if ($this->extClassConf['name'] === TypoScriptTemplateInformationModuleFunctionController::class) {
318 // NEW button
319 $urlParameters = [
320 'id' => $this->id,
321 'template' => 'all',
322 'createExtension' => 'new'
323 ];
324 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
325 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
326 $newButton = $buttonBar->makeLinkButton()
327 ->setHref((string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters))
328 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle'))
329 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
330 'actions-add',
331 Icon::SIZE_SMALL
332 ));
333 $buttonBar->addButton($newButton);
334 } elseif ($this->extClassConf['name'] === TypoScriptTemplateConstantEditorModuleFunctionController::class
335 && !empty($this->MOD_MENU['constant_editor_cat'])) {
336 // SAVE button
337 $saveButton = $buttonBar->makeInputButton()
338 ->setName('_savedok')
339 ->setValue('1')
340 ->setForm('TypoScriptTemplateModuleController')
341 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
342 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
343 'actions-document-save',
344 Icon::SIZE_SMALL
345 ))
346 ->setShowLabelText(true);
347 $buttonBar->addButton($saveButton);
348 } elseif ($this->extClassConf['name'] === TypoScriptTemplateObjectBrowserModuleFunctionController::class) {
349 if (!empty($this->sObj)) {
350 // BACK
351 $urlParameters = [
352 'id' => $this->id
353 ];
354 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
355 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
356 $backButton = $buttonBar->makeLinkButton()
357 ->setHref((string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters))
358 ->setClasses('typo3-goBack')
359 ->setTitle($lang->sL('LLL:EXT:lang/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
360 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
361 'actions-view-go-back',
362 Icon::SIZE_SMALL
363 ));
364 $buttonBar->addButton($backButton);
365 }
366 }
367 }
368 // Shortcut
369 $shortcutButton = $buttonBar->makeShortcutButton()
370 ->setModuleName($this->MCONF['name'])
371 ->setGetVariables(['id', 'route']);
372 $buttonBar->addButton($shortcutButton);
373 }
374
375 // OTHER FUNCTIONS:
376 /**
377 * Wrap title for link in template
378 *
379 * @param string $title
380 * @param string $onlyKey
381 * @return string
382 */
383 public function linkWrapTemplateTitle($title, $onlyKey = '')
384 {
385 $urlParameters = [
386 'id' => $this->id
387 ];
388 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
389 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
390 $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
391 if ($onlyKey) {
392 $title = '<a href="' . htmlspecialchars(($aHref . '&e[' . $onlyKey . ']=1&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\TypoScriptTemplateInformationModuleFunctionController')) . '">' . htmlspecialchars($title) . '</a>';
393 } else {
394 $title = '<a href="' . htmlspecialchars(($aHref . '&e[constants]=1&e[config]=1&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\TypoScriptTemplateInformationModuleFunctionController')) . '">' . htmlspecialchars($title) . '</a>';
395 }
396 return $title;
397 }
398
399 /**
400 * No template
401 *
402 * @param int $newStandardTemplate
403 * @return string
404 */
405 public function noTemplate($newStandardTemplate = 0)
406 {
407 $this->templateService = GeneralUtility::makeInstance(ExtendedTemplateService::class);
408 $this->templateService->init();
409
410 $moduleContent['state'] = InfoboxViewHelper::STATE_INFO;
411
412 // New standard?
413 if ($newStandardTemplate) {
414 $selector = '';
415 $staticsText = '';
416 // Hook to change output, implemented for statictemplates
417 $hookObject = $this->getHookObjectForAction('newStandardTemplateView');
418 if (!empty($hookObject)) {
419 $reference = [
420 'selectorHtml' => &$selector,
421 'staticsText' => &$staticsText
422 ];
423 GeneralUtility::callUserFunction(
424 $hookObject,
425 $reference,
426 $this
427 );
428 $selector = $reference['selectorHtml'];
429 $staticsText = $reference['staticsText'];
430 }
431 // Extension?
432 $moduleContent['staticsText'] = $staticsText;
433 $moduleContent['selector'] = $selector;
434 }
435 // Go to previous Page with Template...
436 $previousPage = $this->templateService->ext_prevPageWithTemplate($this->id, $this->perms_clause);
437 if ($previousPage) {
438 $urlParameters = [
439 'id' => $previousPage['uid']
440 ];
441 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
442 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
443 $previousPage['aHref'] = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
444 $moduleContent['previousPage'] = $previousPage;
445 }
446 $view = $this->getFluidTemplateObject('tstemplate', 'NoTemplate');
447 $view->assign('content', $moduleContent);
448 return $view->render();
449 }
450
451 /**
452 * Render template menu
453 *
454 * @return string
455 */
456 public function templateMenu()
457 {
458 $this->templateService = GeneralUtility::makeInstance(ExtendedTemplateService::class);
459 $this->templateService->init();
460
461 $all = $this->templateService->ext_getAllTemplates($this->id);
462 if (count($all) > 1) {
463 $this->MOD_MENU['templatesOnPage'] = [];
464 foreach ($all as $d) {
465 $this->MOD_MENU['templatesOnPage'][$d['uid']] = $d['title'];
466 }
467 }
468 $this->MOD_SETTINGS = BackendUtility::getModuleData(
469 $this->MOD_MENU,
470 GeneralUtility::_GP('SET'),
471 $this->MCONF['name'],
472 $this->modMenu_type,
473 $this->modMenu_dontValidateList,
474 $this->modMenu_setDefaultList
475 );
476 return BackendUtility::getFuncMenu(
477 $this->id,
478 'SET[templatesOnPage]',
479 $this->MOD_SETTINGS['templatesOnPage'],
480 $this->MOD_MENU['templatesOnPage']
481 );
482 }
483
484 /**
485 * Create template
486 *
487 * @param int $id
488 * @param int $actTemplateId
489 * @return string
490 */
491 public function createTemplate($id, $actTemplateId = 0)
492 {
493 $recData = [];
494 /** @var DataHandler $tce */
495 $tce = GeneralUtility::makeInstance(DataHandler::class);
496
497 if (GeneralUtility::_GP('createExtension')) {
498 $recData['sys_template']['NEW'] = [
499 'pid' => $actTemplateId ? -1 * $actTemplateId : $id,
500 'title' => '+ext'
501 ];
502 $tce->start($recData, []);
503 $tce->process_datamap();
504 } elseif (GeneralUtility::_GP('newWebsite')) {
505 // Hook to handle row data, implemented for statictemplates
506 $hookObject = $this->getHookObjectForAction('newStandardTemplateHandler');
507 if (!empty($hookObject)) {
508 $reference = [
509 'recData' => &$recData,
510 'id' => $id,
511 ];
512 GeneralUtility::callUserFunction(
513 $hookObject,
514 $reference,
515 $this
516 );
517 $recData = $reference['recData'];
518 } else {
519 $recData['sys_template']['NEW'] = [
520 'pid' => $id,
521 'title' => $this->getLanguageService()->getLL('titleNewSite'),
522 'sorting' => 0,
523 'root' => 1,
524 'clear' => 3,
525 'config' => '
526 # Default PAGE object:
527 page = PAGE
528 page.10 = TEXT
529 page.10.value = HELLO WORLD!
530 '
531 ];
532 }
533 $tce->start($recData, []);
534 $tce->process_datamap();
535 $tce->clear_cacheCmd('all');
536 }
537 return $tce->substNEWwithIDs['NEW'];
538 }
539
540 // RENDER LIST of pages with templates, BEGIN
541 /**
542 * Set page in array
543 *
544 * @param array $pArray
545 * @param array $rlArr
546 * @param array $row
547 */
548 public function setInPageArray(&$pArray, $rlArr, $row)
549 {
550 ksort($rlArr);
551 reset($rlArr);
552 if (!$rlArr[0]['uid']) {
553 array_shift($rlArr);
554 }
555 $cEl = current($rlArr);
556 $pArray[$cEl['uid']] = $cEl['title'];
557 array_shift($rlArr);
558 if (!empty($rlArr)) {
559 $key = $cEl['uid'] . '.';
560 if (empty($pArray[$key])) {
561 $pArray[$key] = [];
562 }
563 $this->setInPageArray($pArray[$key], $rlArr, $row);
564 } else {
565 $key = $cEl['uid'] . '_';
566 $pArray[$key] = $row;
567 }
568 }
569
570 /**
571 * Get the list
572 *
573 * @param array $pArray
574 * @param array $lines
575 * @param int $c
576 * @return array
577 */
578 public function renderList($pArray, $lines = [], $c = 0)
579 {
580 if (!is_array($pArray)) {
581 return $lines;
582 }
583
584 $statusCheckedIcon = $this->moduleTemplate->getIconFactory()
585 ->getIcon('status-status-checked', Icon::SIZE_SMALL)->render();
586 $i = 0;
587 foreach ($pArray as $k => $v) {
588 if (MathUtility::canBeInterpretedAsInteger($k)) {
589 $line = [];
590 $key = $k . '_';
591 $line['marginLeft'] = $c * 20;
592 $line['class'] = ($i++ % 2 === 0 ? 'bgColor4' : 'bgColor6');
593 $line['pageTitle'] = GeneralUtility::fixed_lgd_cs($pArray[$k], 30);
594 $line['icon'] = $this->moduleTemplate->getIconFactory()
595 ->getIconForRecord(
596 'pages',
597 BackendUtility::getRecordWSOL('pages', $k),
598 Icon::SIZE_SMALL
599 )->render();
600 if (!empty($pArray[$key])) {
601 $line['href'] = GeneralUtility::linkThisScript(['id' => (int)$k]);
602 $line['title'] = 'ID: ' . (int)$k;
603 $line['count'] = $pArray[$k . '_']['count'];
604 $line['root_max_val'] = ($pArray[$key]['root_max_val'] > 0 ? $statusCheckedIcon : '&nbsp;');
605 $line['root_min_val'] = ($pArray[$key]['root_min_val'] === 0 ? $statusCheckedIcon : '&nbsp;');
606 } else {
607 $line['href'] = '';
608 $line['title'] = '';
609 $line['count'] = '';
610 $line['root_max_val'] = '';
611 $line['root_min_val'] = '';
612 }
613 $lines[] = $line;
614 $lines = $this->renderList($pArray[$k . '.'], $lines, $c + 1);
615 }
616 }
617 return $lines;
618 }
619
620 /**
621 * Returns a new standalone view, shorthand function
622 *
623 * @param string $extensionName
624 * @param string $templateName
625 * @return StandaloneView
626 */
627 protected function getFluidTemplateObject($extensionName, $templateName = 'Main')
628 {
629 /** @var StandaloneView $view */
630 $view = GeneralUtility::makeInstance(StandaloneView::class);
631 $view->getRenderingContext()->getTemplatePaths()->fillDefaultsByPackageName($extensionName);
632 $view->getRenderingContext()->setControllerAction($templateName);
633 $view->getRequest()->setControllerExtensionName('tstemplate');
634 return $view;
635 }
636
637 /**
638 * @param string $action
639 * @return string
640 */
641 protected function getHookObjectForAction($action)
642 {
643 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class][$action])) {
644 return $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class][$action];
645 }
646 return null;
647 }
648 }