b4d6442f768df59f346bdd0d0d510c12df23772c
[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\Routing\UriBuilder;
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\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait;
25 use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
26 use TYPO3\CMS\Core\Database\Connection;
27 use TYPO3\CMS\Core\Database\ConnectionPool;
28 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
29 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
30 use TYPO3\CMS\Core\DataHandling\DataHandler;
31 use TYPO3\CMS\Core\Http\HtmlResponse;
32 use TYPO3\CMS\Core\Imaging\Icon;
33 use TYPO3\CMS\Core\Localization\LanguageService;
34 use TYPO3\CMS\Core\Messaging\FlashMessage;
35 use TYPO3\CMS\Core\Messaging\FlashMessageService;
36 use TYPO3\CMS\Core\Page\PageRenderer;
37 use TYPO3\CMS\Core\Type\Bitmask\Permission;
38 use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
39 use TYPO3\CMS\Core\Utility\GeneralUtility;
40 use TYPO3\CMS\Core\Utility\MathUtility;
41 use TYPO3\CMS\Core\Versioning\VersionState;
42 use TYPO3\CMS\Fluid\View\StandaloneView;
43 use TYPO3\CMS\Fluid\ViewHelpers\Be\InfoboxViewHelper;
44
45 /**
46 * Module: TypoScript Tools
47 * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
48 */
49 class TypoScriptTemplateModuleController
50 {
51 use PublicPropertyDeprecationTrait;
52 use PublicMethodDeprecationTrait;
53
54 /**
55 * @var array
56 */
57 private $deprecatedPublicProperties = [
58 'textExtensions' => 'Using TypoScriptTemplateModuleController::$textExtensions is deprecated and will not be possible anymore in TYPO3 v10.0.',
59 'pageinfo' => 'Using TypoScriptTemplateModuleController::$pageinfo is deprecated and will not be possible anymore in TYPO3 v10.0.',
60 'id' => 'Using TypoScriptTemplateModuleController::$id is deprecated and will not be possible anymore in TYPO3 v10.0.',
61 'modTSconfig' => 'Using TypoScriptTemplateModuleController::$modTSconfig is deprecated and will not be possible anymore in TYPO3 v10.0.',
62 'content' => 'Using TypoScriptTemplateModuleController::$content is deprecated and will not be possible anymore in TYPO3 v10.0.',
63 'extObj' => 'Using TypoScriptTemplateModuleController::$extObj is deprecated and will not be possible anymore in TYPO3 v10.0.',
64 'access' => 'Using TypoScriptTemplateModuleController::$access is deprecated and will not be possible anymore in TYPO3 v10.0.',
65 'perms_clause' => 'Using TypoScriptTemplateModuleController::$perms_clause is deprecated and will not be possible anymore in TYPO3 v10.0.',
66 'extClassConf' => 'Using TypoScriptTemplateModuleController::$extClassConf is deprecated and will not be possible anymore in TYPO3 v10.0.',
67 'edit' => 'Using TypoScriptTemplateModuleController::$edit is deprecated, property will be removed in TYPO3 v10.0.',
68 'modMenu_type' => 'Using TypoScriptTemplateModuleController::$modMenu_type is deprecated, property will be removed in TYPO3 v10.0.',
69 'MCONF' => 'Using TypoScriptTemplateModuleController::$MCONF is deprecated, property will be removed in TYPO3 v10.0.',
70 'CMD' => 'Using TypoScriptTemplateModuleController::$CMD is deprecated, property will be removed in TYPO3 v10.0.',
71 'sObj' => 'Using TypoScriptTemplateModuleController::$sObj is deprecated, property will be removed in TYPO3 v10.0.',
72 ];
73
74 /**
75 * @var array
76 */
77 private $deprecatedPublicMethods = [
78 'getExternalItemConfig' => 'Using TypoScriptTemplateModuleController::getExternalItemConfig() is deprecated and will not be possible anymore in TYPO3 v10.0.',
79 'init' => 'Using TypoScriptTemplateModuleController::init() is deprecated and will not be possible anymore in TYPO3 v10.0.',
80 'clearCache' => 'Using TypoScriptTemplateModuleController::clearCache() is deprecated and will not be possible anymore in TYPO3 v10.0.',
81 'main' => 'Using TypoScriptTemplateModuleController::main() is deprecated and will not be possible anymore in TYPO3 v10.0.',
82 'setInPageArray' => 'Using TypoScriptTemplateModuleController::setInPageArray() is deprecated and will not be possible anymore in TYPO3 v10.0.',
83 'menuConfig' => 'Using TypoScriptTemplateModuleController::menuConfig() is deprecated and will not be possible anymore in TYPO3 v10.0.',
84 'mergeExternalItems' => 'Using TypoScriptTemplateModuleController::mergeExternalItems() is deprecated and will not be possible anymore in TYPO3 v10.0.',
85 'handleExternalFunctionValue' => 'Using TypoScriptTemplateModuleController::handleExternalFunctionValue() is deprecated and will not be possible anymore in TYPO3 v10.0.',
86 'checkExtObj' => 'Using TypoScriptTemplateModuleController::checkExtObj() is deprecated and will not be possible anymore in TYPO3 v10.0.',
87 'extObjContent' => 'Using TypoScriptTemplateModuleController::extObjContent() is deprecated and will not be possible anymore in TYPO3 v10.0.',
88 'getExtObjContent' => 'Using TypoScriptTemplateModuleController::getExtObjContent() is deprecated and will not be possible anymore in TYPO3 v10.0.',
89 'checkSubExtObj' => 'Using TypoScriptTemplateModuleController::checkSubExtObj() is deprecated, method will be removed in TYPO3 v10.0.',
90 'extObjHeader' => 'Using TypoScriptTemplateModuleController::extObjHeader() is deprecated, method will be removed in TYPO3 v10.0.',
91 ];
92
93 /**
94 * @var string
95 */
96 protected $perms_clause;
97
98 /**
99 * @var string
100 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
101 */
102 protected $sObj;
103
104 /**
105 * @var string
106 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
107 */
108 protected $edit;
109
110 /**
111 * @var string
112 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
113 */
114 protected $textExtensions = 'html,htm,txt,css,tmpl,inc,js';
115
116 /**
117 * @var string Written by client classes.
118 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Remove last usages, too.
119 */
120 protected $modMenu_type = '';
121
122 /**
123 * @var string
124 */
125 public $modMenu_dontValidateList = '';
126
127 /**
128 * @var string Written by client classes
129 */
130 public $modMenu_setDefaultList = '';
131
132 /**
133 * @var array
134 */
135 protected $pageinfo = [];
136
137 /**
138 * @var bool
139 */
140 protected $access = false;
141
142 /**
143 * The name of the module
144 *
145 * @var string
146 */
147 protected $moduleName = 'web_ts';
148
149 /**
150 * ModuleTemplate Container
151 *
152 * @var ModuleTemplate
153 */
154 protected $moduleTemplate;
155
156 /**
157 * @var ExtendedTemplateService
158 */
159 protected $templateService;
160
161 /**
162 * Loaded with the global array $MCONF which holds some module configuration from the conf.php file of backend modules.
163 *
164 * @var array
165 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
166 */
167 protected $MCONF = [];
168
169 /**
170 * @var int GET/POST var 'id'
171 */
172 protected $id;
173
174 /**
175 * The value of GET/POST var, 'CMD'
176 *
177 * @var mixed
178 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
179 */
180 protected $CMD;
181
182 /**
183 * The module menu items array. Each key represents a key for which values can range between the items in the array of that key.
184 * Written by client classes.
185 *
186 * @var array
187 */
188 public $MOD_MENU = [
189 'function' => []
190 ];
191
192 /**
193 * Current settings for the keys of the MOD_MENU array, used in client classes
194 *
195 * @see $MOD_MENU
196 * @var array
197 */
198 public $MOD_SETTINGS = [];
199
200 /**
201 * Module TSconfig based on PAGE TSconfig / USER TSconfig
202 *
203 * @var array
204 */
205 protected $modTSconfig;
206
207 /**
208 * Contains module configuration parts from TBE_MODULES_EXT if found
209 *
210 * @see handleExternalFunctionValue()
211 * @var array
212 */
213 protected $extClassConf;
214
215 /**
216 * Generally used for accumulating the output content of backend modules
217 *
218 * @var string
219 */
220 protected $content = '';
221
222 /**
223 * May contain an instance of a 'Function menu module' which connects to this backend module.
224 *
225 * @see checkExtObj()
226 * @var \object
227 */
228 protected $extObj;
229
230 /**
231 * Constructor
232 */
233 public function __construct()
234 {
235 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
236 $this->getLanguageService()->includeLLFile('EXT:tstemplate/Resources/Private/Language/locallang.xlf');
237
238 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
239 $this->MCONF = [
240 'name' => $this->moduleName
241 ];
242 $this->moduleTemplate->addJavaScriptCode(
243 'jumpToUrl',
244 '
245 function jumpToUrl(URL) {
246 window.location.href = URL;
247 return false;
248 }
249 '
250 );
251 }
252
253 /**
254 * Init
255 */
256 protected function init()
257 {
258 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
259 $this->CMD = GeneralUtility::_GP('CMD');
260 $this->menuConfig();
261 $this->handleExternalFunctionValue();
262 $this->id = (int)GeneralUtility::_GP('id');
263 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
264 $this->sObj = GeneralUtility::_GP('sObj');
265 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
266 $this->edit = GeneralUtility::_GP('edit');
267 $this->perms_clause = $this->getBackendUser()->getPagePermsClause(Permission::PAGE_SHOW);
268 }
269
270 /**
271 * Clear cache
272 */
273 protected function clearCache()
274 {
275 if (GeneralUtility::_GP('clear_all_cache')) {
276 $tce = GeneralUtility::makeInstance(DataHandler::class);
277 $tce->start([], []);
278 $tce->clear_cacheCmd('all');
279 }
280 }
281
282 /**
283 * Main
284 */
285 protected function main()
286 {
287 // Access check...
288 // The page will show only if there is a valid page and if this page may be viewed by the user
289 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->perms_clause);
290 $this->access = is_array($this->pageinfo);
291 $view = $this->getFluidTemplateObject('tstemplate');
292 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
293 if ($this->id && $this->access) {
294 $urlParameters = [
295 'id' => $this->id,
296 'template' => 'all'
297 ];
298 $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
299
300 // JavaScript
301 $this->moduleTemplate->addJavaScriptCode(
302 'TSTemplateInlineJS',
303 'function uFormUrl(aname) {
304 document.forms[0].action = ' . GeneralUtility::quoteJSvalue($aHref . '#') . '+aname;
305 }
306 function brPoint(lnumber,t) {
307 window.location.href = '
308 . GeneralUtility::quoteJSvalue(
309 $aHref . '&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\'
310 . 'TypoScriptTemplateObjectBrowserModuleFunctionController&SET[ts_browser_type]='
311 ) . '+(t?"setup":"const")+"&breakPointLN="+lnumber;
312 return false;
313 }
314 if (top.fsMod) top.fsMod.recentIds["web"] = ' . $this->id . ';'
315 );
316 $this->moduleTemplate->getPageRenderer()->addCssInlineBlock(
317 'TSTemplateInlineStyle',
318 'TABLE#typo3-objectBrowser { width: 100%; margin-bottom: 24px; }
319 TABLE#typo3-objectBrowser A { text-decoration: none; }
320 TABLE#typo3-objectBrowser .comment { color: maroon; font-weight: bold; }
321 .ts-typoscript { width: 100%; }
322 .tsob-search-submit {margin-left: 3px; margin-right: 3px;}
323 .tst-analyzer-options { margin:5px 0; }'
324 );
325 // Setting up the context sensitive menu:
326 $this->moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ContextMenu');
327 // Build the module content
328 $view->assign('actionName', $aHref);
329 $view->assign('typoscriptTemplateModuleContent', $this->getExtObjContent());
330 // Setting up the buttons and markers for docheader
331 $this->getButtons();
332 $this->generateMenu();
333 } else {
334 $workspaceId = $this->getBackendUser()->workspace;
335 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
336 ->getQueryBuilderForTable('sys_template');
337 $queryBuilder->getRestrictions()
338 ->removeAll()
339 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
340 $this->applyWorkspaceConstraint(
341 $queryBuilder,
342 'sys_template',
343 $workspaceId
344 );
345 $result = $queryBuilder
346 ->select(
347 'uid',
348 'pid',
349 'title',
350 'sitetitle',
351 'root',
352 'hidden',
353 'starttime',
354 'endtime',
355 't3ver_oid',
356 't3ver_wsid',
357 't3ver_state',
358 't3ver_move_id'
359 )
360 ->from('sys_template')
361 ->orderBy('sys_template.pid')
362 ->addOrderBy('sys_template.sorting')
363 ->execute();
364 $pArray = [];
365 while ($record = $result->fetch()) {
366 BackendUtility::workspaceOL('sys_template', $record, $workspaceId, true);
367 if (empty($record) || VersionState::cast($record['t3ver_state'])->equals(VersionState::DELETE_PLACEHOLDER)) {
368 continue;
369 }
370 $additionalFieldsForRootline = ['sorting', 'hidden', 'fe_group', 'starttime', 'endtime', 'shortcut', 'nav_hide', 'module', 'content_from_pid'];
371 $rootline = BackendUtility::BEgetRootLine($record['pid'], '', true, $additionalFieldsForRootline);
372 $this->setInPageArray($pArray, $rootline, $record);
373 }
374
375 $view->getRenderingContext()->setControllerAction('PageZero');
376 $view->assign('pageTree', $pArray);
377
378 // RENDER LIST of pages with templates, END
379 // Setting up the buttons and markers for docheader
380 $this->getButtons();
381 }
382 $this->content = $view->render();
383 }
384
385 /**
386 * Generates the menu based on $this->MOD_MENU
387 *
388 * @throws \InvalidArgumentException
389 */
390 protected function generateMenu()
391 {
392 $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
393 $menu->setIdentifier('WebFuncJumpMenu');
394 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
395 foreach ($this->MOD_MENU['function'] as $controller => $title) {
396 $item = $menu
397 ->makeMenuItem()
398 ->setHref(
399 (string)$uriBuilder->buildUriFromRoute(
400 $this->moduleName,
401 [
402 'id' => $this->id,
403 'SET' => [
404 'function' => $controller
405 ]
406 ]
407 )
408 )
409 ->setTitle($title);
410 if ($controller === $this->MOD_SETTINGS['function']) {
411 $item->setActive(true);
412 }
413 $menu->addMenuItem($item);
414 }
415 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
416 }
417
418 /**
419 * Injects the request object for the current request or subrequest
420 * Then checks for module functions that have hooked in, and renders menu etc.
421 *
422 * @param ServerRequestInterface $request the current request
423 * @return ResponseInterface the response with the content
424 */
425 public function mainAction(ServerRequestInterface $request): ResponseInterface
426 {
427 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
428 $GLOBALS['SOBE'] = $this;
429
430 $this->init();
431
432 // Checking for first level external objects
433 $this->checkExtObj();
434
435 $this->clearCache();
436 $this->main();
437
438 $this->moduleTemplate->setContent($this->content);
439 return new HtmlResponse($this->moduleTemplate->renderContent());
440 }
441
442 /**
443 * Create the panel of buttons for submitting the form or otherwise perform operations.
444 */
445 protected function getButtons()
446 {
447 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
448 $lang = $this->getLanguageService();
449
450 if ($this->id && $this->access) {
451 // View page
452 $viewButton = $buttonBar->makeLinkButton()
453 ->setHref('#')
454 ->setOnClick(BackendUtility::viewOnClick(
455 $this->pageinfo['uid'],
456 '',
457 BackendUtility::BEgetRootLine($this->pageinfo['uid'])
458 ))
459 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
460 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon('actions-view-page', Icon::SIZE_SMALL));
461 $buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 99);
462
463 if ($this->extClassConf['name'] === TypoScriptTemplateInformationModuleFunctionController::class) {
464 // NEW button
465 $urlParameters = [
466 'id' => $this->id,
467 'template' => 'all',
468 'createExtension' => 'new'
469 ];
470 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
471 $newButton = $buttonBar->makeLinkButton()
472 ->setHref((string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters))
473 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:db_new.php.pagetitle'))
474 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
475 'actions-add',
476 Icon::SIZE_SMALL
477 ));
478 $buttonBar->addButton($newButton);
479 } elseif ($this->extClassConf['name'] === TypoScriptTemplateConstantEditorModuleFunctionController::class
480 && !empty($this->MOD_MENU['constant_editor_cat'])) {
481 // SAVE button
482 $saveButton = $buttonBar->makeInputButton()
483 ->setName('_savedok')
484 ->setValue('1')
485 ->setForm('TypoScriptTemplateModuleController')
486 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveDoc'))
487 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
488 'actions-document-save',
489 Icon::SIZE_SMALL
490 ))
491 ->setShowLabelText(true);
492 $buttonBar->addButton($saveButton);
493 } elseif ($this->extClassConf['name'] === TypoScriptTemplateObjectBrowserModuleFunctionController::class
494 && !empty(GeneralUtility::_GP('sObj'))
495 ) {
496 // back button in edit mode of object browser. "sObj" is set by ExtendedTemplateService
497 $urlParameters = [
498 'id' => $this->id
499 ];
500 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
501 $backButton = $buttonBar->makeLinkButton()
502 ->setHref((string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters))
503 ->setClasses('typo3-goBack')
504 ->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.goBack'))
505 ->setIcon($this->moduleTemplate->getIconFactory()->getIcon(
506 'actions-view-go-back',
507 Icon::SIZE_SMALL
508 ));
509 $buttonBar->addButton($backButton);
510 }
511 }
512 // Shortcut
513 $shortcutButton = $buttonBar->makeShortcutButton()
514 ->setModuleName('web_ts')
515 ->setGetVariables(['id', 'route']);
516 $buttonBar->addButton($shortcutButton);
517 }
518
519 /**
520 * Wrap title for link in template, called from client classes.
521 *
522 * @param string $title
523 * @param string $onlyKey
524 * @return string
525 */
526 public function linkWrapTemplateTitle($title, $onlyKey = '')
527 {
528 $urlParameters = [
529 'id' => $this->id
530 ];
531 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
532 $aHref = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
533 if ($onlyKey) {
534 $title = '<a href="' . htmlspecialchars($aHref . '&e[' . $onlyKey . ']=1&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\TypoScriptTemplateInformationModuleFunctionController') . '">' . htmlspecialchars($title) . '</a>';
535 } else {
536 $title = '<a href="' . htmlspecialchars($aHref . '&e[constants]=1&e[config]=1&SET[function]=TYPO3\\CMS\\Tstemplate\\Controller\\TypoScriptTemplateInformationModuleFunctionController') . '">' . htmlspecialchars($title) . '</a>';
537 }
538 return $title;
539 }
540
541 /**
542 * No template, called from client classes.
543 *
544 * @param int $newStandardTemplate
545 * @return string
546 */
547 public function noTemplate($newStandardTemplate = 0)
548 {
549 $this->templateService = GeneralUtility::makeInstance(ExtendedTemplateService::class);
550
551 $moduleContent['state'] = InfoboxViewHelper::STATE_INFO;
552
553 // New standard?
554 if ($newStandardTemplate) {
555 $selector = '';
556 $staticsText = '';
557 // Hook to change output, implemented for statictemplates
558 $hookObject = $this->getHookObjectForAction('newStandardTemplateView');
559 if (!empty($hookObject)) {
560 $reference = [
561 'selectorHtml' => &$selector,
562 'staticsText' => &$staticsText
563 ];
564 GeneralUtility::callUserFunction(
565 $hookObject,
566 $reference,
567 $this
568 );
569 $selector = $reference['selectorHtml'];
570 $staticsText = $reference['staticsText'];
571 }
572 // Extension?
573 $moduleContent['staticsText'] = $staticsText;
574 $moduleContent['selector'] = $selector;
575 }
576 // Go to previous Page with Template...
577 $previousPage = $this->templateService->ext_prevPageWithTemplate($this->id, $this->perms_clause);
578 if ($previousPage) {
579 $urlParameters = [
580 'id' => $previousPage['uid']
581 ];
582 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
583 $previousPage['aHref'] = (string)$uriBuilder->buildUriFromRoute('web_ts', $urlParameters);
584 $moduleContent['previousPage'] = $previousPage;
585 }
586 $view = $this->getFluidTemplateObject('tstemplate', 'NoTemplate');
587 $view->assign('content', $moduleContent);
588 return $view->render();
589 }
590
591 /**
592 * Render template menu, called from client classes.
593 *
594 * @return string
595 */
596 public function templateMenu()
597 {
598 $this->templateService = GeneralUtility::makeInstance(ExtendedTemplateService::class);
599
600 $all = $this->templateService->ext_getAllTemplates($this->id);
601 if (count($all) > 1) {
602 $this->MOD_MENU['templatesOnPage'] = [];
603 foreach ($all as $d) {
604 $this->MOD_MENU['templatesOnPage'][$d['uid']] = $d['title'];
605 }
606 }
607 $this->MOD_SETTINGS = BackendUtility::getModuleData(
608 $this->MOD_MENU,
609 GeneralUtility::_GP('SET'),
610 'web_ts',
611 $this->modMenu_type,
612 $this->modMenu_dontValidateList,
613 $this->modMenu_setDefaultList
614 );
615 return BackendUtility::getFuncMenu(
616 $this->id,
617 'SET[templatesOnPage]',
618 $this->MOD_SETTINGS['templatesOnPage'],
619 $this->MOD_MENU['templatesOnPage']
620 );
621 }
622
623 /**
624 * Create template, called from client classes.
625 *
626 * @param int $id
627 * @param int $actTemplateId
628 * @return string
629 */
630 public function createTemplate($id, $actTemplateId = 0)
631 {
632 $recData = [];
633 $tce = GeneralUtility::makeInstance(DataHandler::class);
634
635 if (GeneralUtility::_GP('createExtension')) {
636 $recData['sys_template']['NEW'] = [
637 'pid' => $actTemplateId ? -1 * $actTemplateId : $id,
638 'title' => '+ext'
639 ];
640 $tce->start($recData, []);
641 $tce->process_datamap();
642 } elseif (GeneralUtility::_GP('newWebsite')) {
643 // Hook to handle row data, implemented for statictemplates
644 $hookObject = $this->getHookObjectForAction('newStandardTemplateHandler');
645 if (!empty($hookObject)) {
646 $reference = [
647 'recData' => &$recData,
648 'id' => $id,
649 ];
650 GeneralUtility::callUserFunction(
651 $hookObject,
652 $reference,
653 $this
654 );
655 $recData = $reference['recData'];
656 } else {
657 $recData['sys_template']['NEW'] = [
658 'pid' => $id,
659 'title' => $this->getLanguageService()->getLL('titleNewSite'),
660 'sorting' => 0,
661 'root' => 1,
662 'clear' => 3,
663 'config' => '
664 # Default PAGE object:
665 page = PAGE
666 page.10 = TEXT
667 page.10.value = HELLO WORLD!
668 '
669 ];
670 }
671 $tce->start($recData, []);
672 $tce->process_datamap();
673 $tce->clear_cacheCmd('all');
674 }
675 return $tce->substNEWwithIDs['NEW'];
676 }
677
678 /**
679 * Set page in array
680 * To render list of page tree with templates
681 *
682 * @param array $pArray Multidimensional array of page tree with template records
683 * @param array $rlArr Rootline array
684 * @param array $row Record of sys_template
685 */
686 protected function setInPageArray(&$pArray, $rlArr, $row)
687 {
688 ksort($rlArr);
689 reset($rlArr);
690 if (!$rlArr[0]['uid']) {
691 array_shift($rlArr);
692 }
693 $cEl = current($rlArr);
694 if (empty($pArray[$cEl['uid']])) {
695 $pArray[$cEl['uid']] = $cEl;
696 }
697 array_shift($rlArr);
698 if (!empty($rlArr)) {
699 if (empty($pArray[$cEl['uid']]['_nodes'])) {
700 $pArray[$cEl['uid']]['_nodes'] = [];
701 }
702 $this->setInPageArray($pArray[$cEl['uid']]['_nodes'], $rlArr, $row);
703 } else {
704 $pArray[$cEl['uid']]['_templates'][] = $row;
705 }
706 uasort($pArray, function ($a, $b) {
707 return $a['sorting'] - $b['sorting'];
708 });
709 }
710
711 /**
712 * Get the list
713 *
714 * @param array $pArray
715 * @param array $lines
716 * @param int $c
717 * @return array
718 * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0
719 */
720 public function renderList($pArray, $lines = [], $c = 0)
721 {
722 trigger_error(
723 'The method `TYPO3\CMS\Tstemplate\Controller\TypoScriptTemplateModuleController::renderList` has been deprecated and should not be used any longer, this method will be removed in TYPO3 v10.0',
724 E_USER_DEPRECATED
725 );
726
727 if (!is_array($pArray)) {
728 return $lines;
729 }
730
731 $statusCheckedIcon = $this->moduleTemplate->getIconFactory()
732 ->getIcon('status-status-checked', Icon::SIZE_SMALL)->render();
733 foreach ($pArray as $k => $v) {
734 if (MathUtility::canBeInterpretedAsInteger($k)) {
735 $line = [];
736 $key = $k . '_';
737 $line['marginLeft'] = $c * 20;
738 $line['pageTitle'] = GeneralUtility::fixed_lgd_cs($pArray[$k], 30);
739 $line['icon'] = $this->moduleTemplate->getIconFactory()
740 ->getIconForRecord(
741 'pages',
742 BackendUtility::getRecordWSOL('pages', $k),
743 Icon::SIZE_SMALL
744 )->render();
745 if (!empty($pArray[$key])) {
746 $line['href'] = GeneralUtility::linkThisScript(['id' => (int)$k]);
747 $line['title'] = 'ID: ' . (int)$k;
748 $line['count'] = $pArray[$k . '_']['count'];
749 $line['root_max_val'] = ($pArray[$key]['root_max_val'] > 0 ? $statusCheckedIcon : '&nbsp;');
750 $line['root_min_val'] = ($pArray[$key]['root_min_val'] === 0 ? $statusCheckedIcon : '&nbsp;');
751 } else {
752 $line['href'] = '';
753 $line['title'] = '';
754 $line['count'] = '';
755 $line['root_max_val'] = '';
756 $line['root_min_val'] = '';
757 }
758 $lines[] = $line;
759 $lines = $this->renderList($pArray[$k . '.'], $lines, $c + 1);
760 }
761 }
762 return $lines;
763 }
764
765 /**
766 * Returns a new standalone view, shorthand function
767 *
768 * @param string $extensionName
769 * @param string $templateName
770 * @return StandaloneView
771 */
772 protected function getFluidTemplateObject($extensionName, $templateName = 'Main')
773 {
774 $view = GeneralUtility::makeInstance(StandaloneView::class);
775 $view->getRenderingContext()->getTemplatePaths()->fillDefaultsByPackageName($extensionName);
776 $view->getRenderingContext()->setControllerAction($templateName);
777 $view->getRequest()->setControllerExtensionName('tstemplate');
778 return $view;
779 }
780
781 /**
782 * @param QueryBuilder $queryBuilder
783 * @param string $tableName
784 * @param int $workspaceId
785 */
786 protected function applyWorkspaceConstraint(
787 QueryBuilder $queryBuilder,
788 string $tableName,
789 int $workspaceId
790 ) {
791 if (empty($GLOBALS['TCA'][$tableName]['ctrl']['versioningWS'])) {
792 return;
793 }
794
795 $workspaceIds = [0];
796 if ($workspaceId > 0) {
797 $workspaceIds[] = $workspaceId;
798 }
799 $queryBuilder->andWhere(
800 $queryBuilder->expr()->in(
801 't3ver_wsid',
802 $queryBuilder->createNamedParameter($workspaceIds, Connection::PARAM_INT_ARRAY)
803 ),
804 $queryBuilder->expr()->neq(
805 'pid',
806 $queryBuilder->createNamedParameter(-1, \PDO::PARAM_INT)
807 )
808 );
809 }
810
811 /**
812 * @param string $action
813 * @return string
814 */
815 protected function getHookObjectForAction($action)
816 {
817 if (!empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class][$action])) {
818 return $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class][$action];
819 }
820 return null;
821 }
822
823 /**
824 * Initializes the internal MOD_MENU array setting and unsetting items based on various conditions. It also merges in external menu items from the global array TBE_MODULES_EXT (see mergeExternalItems())
825 * Then MOD_SETTINGS array is cleaned up (see \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData()) so it contains only valid values. It's also updated with any SET[] values submitted.
826 * Also loads the modTSconfig internal variable.
827 *
828 * @see init(), $MOD_MENU, $MOD_SETTINGS, \TYPO3\CMS\Backend\Utility\BackendUtility::getModuleData(), mergeExternalItems()
829 */
830 protected function menuConfig()
831 {
832 // Page / user TSconfig settings and blinding of menu-items
833 $this->modTSconfig['properties'] = BackendUtility::getPagesTSconfig($this->id)['mod.']['web_ts.'] ?? [];
834 $this->MOD_MENU['function'] = $this->mergeExternalItems('web_ts', 'function', $this->MOD_MENU['function']);
835 $blindActions = $this->modTSconfig['properties']['menu.']['function.'] ?? [];
836 foreach ($blindActions as $key => $value) {
837 if (!$value && array_key_exists($key, $this->MOD_MENU['function'])) {
838 unset($this->MOD_MENU['function'][$key]);
839 }
840 }
841 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), 'web_ts', $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
842 }
843
844 /**
845 * Merges menu items from global array $TBE_MODULES_EXT
846 *
847 * @param string $modName Module name for which to find value
848 * @param string $menuKey Menu key, eg. 'function' for the function menu.
849 * @param array $menuArr The part of a MOD_MENU array to work on.
850 * @return array Modified array part.
851 * @internal
852 * @see \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), menuConfig()
853 */
854 protected function mergeExternalItems($modName, $menuKey, $menuArr)
855 {
856 $mergeArray = $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
857 if (is_array($mergeArray)) {
858 foreach ($mergeArray as $k => $v) {
859 if (((string)$v['ws'] === '' || $this->getBackendUser()->workspace === 0 && GeneralUtility::inList($v['ws'], 'online')) || $this->getBackendUser()->workspace === -1 && GeneralUtility::inList($v['ws'], 'offline') || $this->getBackendUser()->workspace > 0 && GeneralUtility::inList($v['ws'], 'custom')) {
860 $menuArr[$k] = $this->getLanguageService()->sL($v['title']);
861 }
862 }
863 }
864 return $menuArr;
865 }
866
867 /**
868 * Loads $this->extClassConf with the configuration for the CURRENT function of the menu.
869 *
870 * @param string $MM_key The key to MOD_MENU for which to fetch configuration. 'function' is default since it is first and foremost used to get information per "extension object" (I think that is what its called)
871 * @param string $MS_value The value-key to fetch from the config array. If NULL (default) MOD_SETTINGS[$MM_key] will be used. This is useful if you want to force another function than the one defined in MOD_SETTINGS[function]. Call this in init() function of your Script Class: handleExternalFunctionValue('function', $forcedSubModKey)
872 * @see getExternalItemConfig(), init()
873 */
874 protected function handleExternalFunctionValue($MM_key = 'function', $MS_value = null)
875 {
876 if ($MS_value === null) {
877 $MS_value = $this->MOD_SETTINGS[$MM_key];
878 }
879 $this->extClassConf = $this->getExternalItemConfig('web_ts', $MM_key, $MS_value);
880 }
881
882 /**
883 * Returns configuration values from the global variable $TBE_MODULES_EXT for the module given.
884 * For example if the module is named "web_info" and the "function" key ($menuKey) of MOD_SETTINGS is "stat" ($value) then you will have the values of $TBE_MODULES_EXT['webinfo']['MOD_MENU']['function']['stat'] returned.
885 *
886 * @param string $modName Module name
887 * @param string $menuKey Menu key, eg. "function" for the function menu. See $this->MOD_MENU
888 * @param string $value Optionally the value-key to fetch from the array that would otherwise have been returned if this value was not set. Look source...
889 * @return mixed The value from the TBE_MODULES_EXT array.
890 * @see handleExternalFunctionValue()
891 */
892 protected function getExternalItemConfig($modName, $menuKey, $value = '')
893 {
894 if (isset($GLOBALS['TBE_MODULES_EXT'][$modName])) {
895 return (string)$value !== ''
896 ? $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey][$value]
897 : $GLOBALS['TBE_MODULES_EXT'][$modName]['MOD_MENU'][$menuKey];
898 }
899 return null;
900 }
901
902 /**
903 * Creates an instance of the class found in $this->extClassConf['name'] in $this->extObj if any (this should hold three keys, "name", "path" and "title" if a "Function menu module" tries to connect...)
904 * This value in extClassConf might be set by an extension (in an ext_tables/ext_localconf file) which thus "connects" to a module.
905 * The array $this->extClassConf is set in handleExternalFunctionValue() based on the value of MOD_SETTINGS[function]
906 * If an instance is created it is initiated with $this passed as value and $this->extClassConf as second argument. Further the $this->MOD_SETTING is cleaned up again after calling the init function.
907 *
908 * @see handleExternalFunctionValue(), \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::insertModuleFunction(), $extObj
909 */
910 protected function checkExtObj()
911 {
912 if (is_array($this->extClassConf) && $this->extClassConf['name']) {
913 $this->extObj = GeneralUtility::makeInstance($this->extClassConf['name']);
914 $this->extObj->init($this);
915 // Re-write:
916 $this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), 'web_ts', $this->modMenu_type, $this->modMenu_dontValidateList, $this->modMenu_setDefaultList);
917 }
918 }
919
920 /**
921 * Calls the checkExtObj function in sub module if present.
922 *
923 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
924 */
925 protected function checkSubExtObj()
926 {
927 if (is_object($this->extObj) && is_callable([$this->extObj, 'checkExtObj'])) {
928 $this->extObj->checkExtObj();
929 }
930 }
931
932 /**
933 * Calls the 'header' function inside the "Function menu module" if present.
934 * A header function might be needed to add JavaScript or other stuff in the head.
935 * This can't be done in the main function because the head is already written.
936 *
937 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
938 */
939 protected function extObjHeader()
940 {
941 if (is_callable([$this->extObj, 'head'])) {
942 $this->extObj->head();
943 }
944 }
945
946 /**
947 * Calls the 'main' function inside the "Function menu module" if present
948 */
949 protected function extObjContent()
950 {
951 if ($this->extObj === null) {
952 $flashMessage = GeneralUtility::makeInstance(
953 FlashMessage::class,
954 $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:no_modules_registered'),
955 $this->getLanguageService()->getLL('title'),
956 FlashMessage::ERROR
957 );
958 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
959 /** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
960 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
961 $defaultFlashMessageQueue->enqueue($flashMessage);
962 } else {
963 if (is_callable([$this->extObj, 'main'])) {
964 $this->content .= $this->extObj->main();
965 }
966 }
967 }
968
969 /**
970 * Return the content of the 'main' function inside the "Function menu module" if present
971 *
972 * @return string
973 */
974 protected function getExtObjContent()
975 {
976 $savedContent = $this->content;
977 $this->content = '';
978 $this->extObjContent();
979 $newContent = $this->content;
980 $this->content = $savedContent;
981 return $newContent;
982 }
983
984 /**
985 * Returns the Language Service
986 * @return LanguageService
987 */
988 protected function getLanguageService(): LanguageService
989 {
990 return $GLOBALS['LANG'];
991 }
992
993 /**
994 * Returns the Backend User
995 * @return BackendUserAuthentication
996 */
997 protected function getBackendUser(): BackendUserAuthentication
998 {
999 return $GLOBALS['BE_USER'];
1000 }
1001
1002 /**
1003 * @return PageRenderer
1004 */
1005 protected function getPageRenderer(): PageRenderer
1006 {
1007 return GeneralUtility::makeInstance(PageRenderer::class);
1008 }
1009 }