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