Commit 5399ea63 authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Refactor ext:tstemplate controllers

With the refactoring of the '3rd level' backend module
API, we can finally clean up the Template related
backend controllers.

Notable UI changes:
* There was a rather hidden functionality in the template
  module: The "Template record overview" that lists all
  pages that have sys_template records, is only rendered
  when the root page "Page zero" is selected. This can be
  hard to find and is contrary to all other backend modules
  where sub-functionality is selected using the drop down
  in the doc header. The patch changes this: The template
  record overview is now always shown as 5th element in the
  drop down. It currently renders the same output on all
  pages, but could be made page-aware later.
* The TypoScript Object Browser has a detail view to edit
  single properties, which then write to a sys_template
  record. This view also allows to add the edited path to
  an "Object List" if it has sub properties. This is
  remembered for the given user. The user can then select
  this path in the object browser tree view to limit the
  view to this section of the tree. All in all, this is a
  rather hidden and pretty much unknown detail functionality
  of limited use. This functionality has been removed.

Patch overview:
* Implement the "Template record overview" as new
  controller.
* Resolve "controller extends controller" inheritance and
  introduce an abstract controller with common methods of
  client classes. Methods don't add class state.
* Heavily reduce class state to a minimum.
* Improve separation of controller and view concerns.
* Split some templates to partials.
* Leverage dependency incjection correctly: Abstract uses
  inject* methods, client classes use constructor DI.
* Remove user access checks since the entire Template
  module is 'admin only', which is checked by middlewares.
* Various general clean ups, variable renamings, better
  structured methods.

Change-Id: I64cb7ea6275af491233ff7ee525c9260e5da6627
Resolves: #97591
Related: #97135
Releases: main
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/74550

Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
parent 87feed90
...@@ -72,6 +72,8 @@ class ModuleData ...@@ -72,6 +72,8 @@ class ModuleData
* Cleans a single property by the given allowed list. First fallback * Cleans a single property by the given allowed list. First fallback
* is the default data list. If this list does also not contain an * is the default data list. If this list does also not contain an
* allowed value, the first value from the allowed list is taken. * allowed value, the first value from the allowed list is taken.
*
* @return bool True if something has been cleaned up
*/ */
public function clean(string $propertyName, array $allowedValues): bool public function clean(string $propertyName, array $allowedValues): bool
{ {
......
...@@ -580,7 +580,9 @@ class BackendUtility ...@@ -580,7 +580,9 @@ class BackendUtility
} }
/** /**
* Returns a page record (of page with $id) with an extra field "_thePath" set to the record path IF the WHERE clause, $perms_clause, selects the record. Thus is works as an access check that returns a page record if access was granted, otherwise not. * Returns a page record (of page with $id) with an extra field "_thePath" set to the record path if
* the WHERE clause $perms_clause selects the record. This works as an access check that returns a page
* record if access was granted, otherwise false.
* If $id is zero a pseudo root-page with "_thePath" set is returned IF the current BE_USER is admin. * If $id is zero a pseudo root-page with "_thePath" set is returned IF the current BE_USER is admin.
* In any case ->isInWebMount must return TRUE for the user (regardless of $perms_clause) * In any case ->isInWebMount must return TRUE for the user (regardless of $perms_clause)
* *
......
...@@ -153,11 +153,11 @@ class LanguageService ...@@ -153,11 +153,11 @@ class LanguageService
} }
/** /**
* splitLabel function * Main and most often used method.
* *
* All translations are based on $LOCAL_LANG variables. * Resolve strings like these:
* 'language-splitted' labels can therefore refer to a local-lang file + index. * 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.depth_0'
* Refer to 'Inside TYPO3' for more details * This looks up the given .xlf file path in the 'core' extension for label 'labels.depth_0'
* *
* @param string $input Label key/reference * @param string $input Label key/reference
* @return string * @return string
......
...@@ -160,6 +160,7 @@ class SiteModuleCest ...@@ -160,6 +160,7 @@ class SiteModuleCest
$I->click('Template'); $I->click('Template');
$pageTree->openPath(['styleguide TCA demo']); $pageTree->openPath(['styleguide TCA demo']);
$I->switchToContentFrame(); $I->switchToContentFrame();
$I->selectOption('div.module-docheader select.t3-js-jumpMenuBox', 'Info/Modify');
$I->waitForText('Create new website'); $I->waitForText('Create new website');
$I->amGoingTo('Create a new template for the root page'); $I->amGoingTo('Create a new template for the root page');
...@@ -204,6 +205,8 @@ page.10.value = This is a default text for default rendering without dynamic con ...@@ -204,6 +205,8 @@ page.10.value = This is a default text for default rendering without dynamic con
$pageTree->openPath(['styleguide TCA demo']); $pageTree->openPath(['styleguide TCA demo']);
$I->wait(0.2); $I->wait(0.2);
$I->switchToContentFrame(); $I->switchToContentFrame();
$I->selectOption('div.module-docheader select.t3-js-jumpMenuBox', 'Info/Modify');
$I->waitForText('Edit the whole template record');
$I->click('Edit the whole template record'); $I->click('Edit the whole template record');
$I->waitForElement('#EditDocumentController'); $I->waitForElement('#EditDocumentController');
$I->click('Delete'); $I->click('Delete');
...@@ -258,7 +261,7 @@ page.10.value = This is a default text for default rendering without dynamic con ...@@ -258,7 +261,7 @@ page.10.value = This is a default text for default rendering without dynamic con
$I->waitForElement($saveButtonLink, 30); $I->waitForElement($saveButtonLink, 30);
$I->click($saveButtonLink); $I->click($saveButtonLink);
$I->amGoingTo('Create and delete new site language. Verify "Placehoder" is not added to selector'); $I->amGoingTo('Create and delete new site language. Verify "Placeholder" is not added to selector');
$I->click('Languages'); $I->click('Languages');
$I->click('Create new language'); $I->click('Create new language');
$I->waitForElementVisible('div.inlineIsNewRecord'); $I->waitForElementVisible('div.inlineIsNewRecord');
......
...@@ -46,13 +46,14 @@ class TemplateCest ...@@ -46,13 +46,14 @@ class TemplateCest
$I->switchToContentFrame(); $I->switchToContentFrame();
$I->waitForElementVisible('#ts-overview'); $I->waitForElementVisible('#ts-overview');
$I->see('This is an overview of the pages in the database containing one or more template records. Click a page title to go to the page.'); $I->see('This is a global overview of all pages in the database containing one or more template records.');
$I->wantTo('show templates overview on website root page (uid = 1 and pid = 0)'); $I->wantTo('show templates overview on website root page (uid = 1 and pid = 0)');
$I->switchToMainFrame(); $I->switchToMainFrame();
// click on website root page // click on website root page
$I->clickWithLeftButton('//*[text()=\'styleguide TCA demo\']'); $I->clickWithLeftButton('//*[text()=\'styleguide TCA demo\']');
$I->switchToContentFrame(); $I->switchToContentFrame();
$I->selectOption('div.module-docheader select.t3-js-jumpMenuBox', 'Info/Modify');
$I->waitForText('No template'); $I->waitForText('No template');
$I->see('There was no template on this page!'); $I->see('There was no template on this page!');
$I->see('You need to create a template record below in order to edit your configuration.'); $I->see('You need to create a template record below in order to edit your configuration.');
...@@ -75,7 +76,6 @@ class TemplateCest ...@@ -75,7 +76,6 @@ class TemplateCest
$I->switchToContentFrame(); $I->switchToContentFrame();
$I->waitForText('Create new website'); $I->waitForText('Create new website');
$I->click("//input[@name='newWebsite']"); $I->click("//input[@name='newWebsite']");
$I->waitForText('Edit constants for template');
$I->wantTo('change to Info/Modify and see the template overview table'); $I->wantTo('change to Info/Modify and see the template overview table');
$I->selectOption('.t3-js-jumpMenuBox', 'Info/Modify'); $I->selectOption('.t3-js-jumpMenuBox', 'Info/Modify');
...@@ -208,32 +208,4 @@ class TemplateCest ...@@ -208,32 +208,4 @@ class TemplateCest
$I->waitForText('CONSTANTS ROOT'); $I->waitForText('CONSTANTS ROOT');
$I->seeInSource('<strong class="text-danger">styles</strong>'); $I->seeInSource('<strong class="text-danger">styles</strong>');
} }
/**
* @depends addANewSiteTemplate
*/
public function useObjectListInObjectBrowser(ApplicationTester $I): void
{
$I->wantTo('Open the TypoScript Object Browser and use the object list.');
$I->switchToMainFrame();
$I->clickWithLeftButton('//*[text()=\'styleguide TCA demo\']');
$I->switchToContentFrame();
$I->amGoingTo('Switch to object browser.');
$I->selectOption('.t3-js-jumpMenuBox', 'TypoScript Object Browser');
$I->waitForText('CONSTANTS ROOT');
$I->selectOption('select[name="ts_browser_type"]', 'Setup');
$I->waitForText('SETUP ROOT');
$I->click('tt_content');
$I->waitForText('Edit object/property value');
$I->amGoingTo('add tt_content to object list');
$I->click('Add key "tt_content" to Object List');
$I->see('Remove key from OL');
$I->see('key');
$I->amGoingTo('verify "all" can still be selected and shows full setup.');
$I->selectOption('select[name="ts_browser_toplevel_setup"]', 'all');
$I->seeInSource('<i class="text-muted"># Content element rendering</i>');
}
} }
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Tstemplate\Controller;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\PreviewUriBuilder;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\WorkspaceRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Abstract class with helper methods for single 3rd level Template module controllers.
*
* @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
*/
abstract class AbstractTemplateModuleController
{
protected IconFactory $iconFactory;
protected UriBuilder $uriBuilder;
protected ConnectionPool $connectionPool;
public function injectIconFactory(IconFactory $iconFactory): void
{
$this->iconFactory = $iconFactory;
}
public function injectUriBuilder(UriBuilder $uriBuilder)
{
$this->uriBuilder = $uriBuilder;
}
public function injectConnectionPool(ConnectionPool $connectionPool)
{
$this->connectionPool = $connectionPool;
}
protected function addPreviewButtonToDocHeader(ModuleTemplate $view, int $pageId, int $dokType): void
{
$languageService = $this->getLanguageService();
$buttonBar = $view->getDocHeaderComponent()->getButtonBar();
// Don't add preview button for sysfolders and recycler by default, and look up TS config options
$excludedDokTypes = [
PageRepository::DOKTYPE_RECYCLER,
PageRepository::DOKTYPE_SYSFOLDER,
PageRepository::DOKTYPE_SPACER,
];
$pagesTsConfig = BackendUtility::getPagesTSconfig($pageId);
if (isset($pagesTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'])) {
$excludedDokTypes = GeneralUtility::intExplode(
',',
$pagesTsConfig['TCEMAIN.']['preview.']['disableButtonForDokType'],
true
);
}
if ($pageId && !in_array($dokType, $excludedDokTypes, true)) {
$previewDataAttributes = PreviewUriBuilder::create($pageId)
->withRootLine(BackendUtility::BEgetRootLine($pageId))
->buildDispatcherDataAttributes();
$viewButton = $buttonBar->makeLinkButton()
->setHref('#')
->setDataAttributes($previewDataAttributes ?? [])
->setTitle($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL));
$buttonBar->addButton($viewButton, ButtonBar::BUTTON_POSITION_LEFT, 99);
}
}
protected function addShortcutButtonToDocHeader(ModuleTemplate $view, string $moduleIdentifier, array $pageInfo, int $pageId): void
{
$languageService = $this->getLanguageService();
$buttonBar = $view->getDocHeaderComponent()->getButtonBar();
$shortcutTitle = sprintf(
'%s: %s [%d]',
$languageService->sL('LLL:EXT:tstemplate/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tablabel'),
BackendUtility::getRecordTitle('pages', $pageInfo),
$pageId
);
$shortcutButton = $buttonBar->makeShortcutButton()
->setRouteIdentifier($moduleIdentifier)
->setDisplayName($shortcutTitle)
->setArguments(['id' => $pageId]);
$buttonBar->addButton($shortcutButton);
}
/**
* Create template if requested.
*/
protected function createTemplateIfRequested(ServerRequestInterface $request, int $pageId, int $afterExistingTemplateId = 0): int
{
$languageService = $this->getLanguageService();
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$recordData = [];
if ($request->getParsedBody()['createExtension'] ?? $request->getQueryParams()['createExtension'] ?? false) {
$recordData['sys_template']['NEW'] = [
'pid' => $afterExistingTemplateId ? -1 * $afterExistingTemplateId : $pageId,
'title' => '+ext',
];
$dataHandler->start($recordData, []);
$dataHandler->process_datamap();
} elseif ($request->getParsedBody()['newWebsite'] ?? $request->getQueryParams()['newWebsite'] ?? false) {
$recordData['sys_template']['NEW'] = [
'pid' => $pageId,
'title' => $languageService->sL('LLL:EXT:tstemplate/Resources/Private/Language/locallang.xlf:titleNewSite'),
'sorting' => 0,
'root' => 1,
'clear' => 3,
'config' => "\n"
. "# Default PAGE object:\n"
. "page = PAGE\n"
. "page.10 = TEXT\n"
. "page.10.value = HELLO WORLD!\n",
];
$dataHandler->start($recordData, []);
$dataHandler->process_datamap();
}
return (int)($dataHandler->substNEWwithIDs['NEW'] ?? 0);
}
/**
* Get closest page row that has a template up in rootline
*/
protected function getClosestAncestorPageWithTemplateRecord(int $pageId): array
{
$rootLine = BackendUtility::BEgetRootLine($pageId);
foreach ($rootLine as $rootlineNode) {
if ($this->getFirstTemplateRecordOnPage((int)$rootlineNode['uid'])) {
return $rootlineNode;
}
}
return [];
}
/**
* Get an array of all template records on a page.
*/
protected function getAllTemplateRecordsOnPage(int $pageId): array
{
if (!$pageId) {
return [];
}
$result = $this->getTemplateQueryBuilder($pageId)->executeQuery();
$templateRows = [];
while ($row = $result->fetchAssociative()) {
BackendUtility::workspaceOL('sys_template', $row);
if (is_array($row)) {
$templateRows[] = $row;
}
}
return $templateRows;
}
/**
* Get a single sys_template record attached to a single page.
* If multiple template records are on this page, the first (order by sorting)
* record will be returned, unless a specific template uid is specified via $templateUid
*
* @param int $pageId The pid to select sys_template records from
* @param int $templateUid Optional template uid
* @return array<string,mixed>|false Returns the template record or false if none was found
*/
protected function getFirstTemplateRecordOnPage(int $pageId, int $templateUid = 0): array|false
{
if (empty($pageId)) {
return false;
}
$queryBuilder = $this->getTemplateQueryBuilder($pageId)->setMaxResults(1);
if ($templateUid) {
$queryBuilder->andWhere(
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($templateUid, \PDO::PARAM_INT))
);
}
$row = $queryBuilder->executeQuery()->fetchAssociative();
BackendUtility::workspaceOL('sys_template', $row);
return $row;
}
/**
* Helper method to prepare the query builder for getting sys_template records from a given pid.
*/
protected function getTemplateQueryBuilder(int $pid): QueryBuilder
{
$backendUser = $this->getBackendUser();
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('sys_template');
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class))
->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $backendUser->workspace));
return $queryBuilder->select('*')
->from('sys_template')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT))
)
->orderBy($GLOBALS['TCA']['sys_template']['ctrl']['sortby']);
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
...@@ -19,8 +19,12 @@ namespace TYPO3\CMS\Tstemplate\Controller; ...@@ -19,8 +19,12 @@ namespace TYPO3\CMS\Tstemplate\Controller;
use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Module\ModuleData;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\DataHandling\DataHandler; use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService; use TYPO3\CMS\Core\TypoScript\ExtendedTemplateService;
use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser; use TYPO3\CMS\Core\TypoScript\Parser\ConstantConfigurationParser;
...@@ -31,127 +35,135 @@ use TYPO3\CMS\Core\Utility\RootlineUtility; ...@@ -31,127 +35,135 @@ use TYPO3\CMS\Core\Utility\RootlineUtility;
* TypoScript Constant editor * TypoScript Constant editor
* @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API. * @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
*/ */
class TypoScriptConstantEditorController extends TypoScriptTemplateModuleController class ConstantEditorController extends AbstractTemplateModuleController
{ {
protected array $categories = [ protected array $categories = [
// Constants of superior importance for the template-layout. This is dimensions, imagefiles and enabling of various features.
//The most basic constants, which you would almost always want to configure.
'basic' => [], 'basic' => [],
// Constants of superior importance for the template-layout. This is dimensions, imagefiles and enabling of various features. The most basic constants, which you would almost always want to configure.
'menu' => [],
// Menu setup. This includes fontfiles, sizes, background images. Depending on the menutype. // Menu setup. This includes fontfiles, sizes, background images. Depending on the menutype.
'content' => [], 'menu' => [],
// All constants related to the display of pagecontent elements // All constants related to the display of pagecontent elements
'page' => [], 'content' => [],
// General configuration like metatags, link targets // General configuration like metatags, link targets
'advanced' => [], 'page' => [],
// Advanced functions, which are used very seldom. // Advanced functions, which are used very seldom.
'advanced' => [],
'all' => [], 'all' => [],
]; ];
protected ExtendedTemplateService $templateService;
protected array $constants = []; protected array $constants = [];
protected ConstantConfigurationParser $constantParser; public function __construct(
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
private readonly ConstantConfigurationParser $constantParser,
) {
}
public function handleRequest(ServerRequestInterface $request): ResponseInterface public function handleRequest(ServerRequestInterface $request): ResponseInterface
{ {
$this->constantParser = GeneralUtility::makeInstance(ConstantConfigurationParser::class); $languageService = $this->getLanguageService();
$this->init($request); $backendUser = $this->getBackendUser();
// Fallback to regular module when on root level
if ($this->id === 0) { $currentModule = $request->getAttribute('module');
return $this->overviewAction(); $currentModuleIdentifier = $currentModule->getIdentifier();
} $moduleData = $request->getAttribute('moduleData');
// Create extension template if ($moduleData->cleanUp([])) {
$this->createTemplate(); $backendUser->pushModuleData($currentModuleIdentifier, $moduleData->toArray());
// Checking for more than one template an if, set a menu...
$manyTemplatesMenu = $this->templateMenu();
$selectedTemplateRecord = 0;
if ($manyTemplatesMenu) {
$selectedTemplateRecord = (int)$this->moduleData->get('templatesOnPage');
} }
// initialize $pageId = (int)($request->getQueryParams()['id'] ?? 0);
$existTemplate = $this->initialize_editor($selectedTemplateRecord); if ($pageId === 0) {
if ($existTemplate) { // Redirect to template record overview if on page 0.
$assigns['templateRecord'] = $this->templateRow; return new RedirectResponse($this->uriBuilder->buildUriFromRoute('web_typoscript_recordsoverview'));
$assigns['manyTemplatesMenu'] = $manyTemplatesMenu; }
$pageRecord = BackendUtility::readPageAccess($pageId, '1=1') ?: [];
$saveId = empty($this->templateRow['_ORIG_uid']) ? $this->templateRow['uid'] : $this->templateRow['_ORIG_uid']; $this->createTemplateIfRequested($request, $pageId);
// Update template ?
if ($this->request->getParsedBody()['_savedok'] ?? false) { $allTemplatesOnPage = $this->getAllTemplateRecordsOnPage($pageId);
$constantsHaveChanged = $this->templateService->ext_procesInput($this->request->getParsedBody(), $this->constants); if ($moduleData->clean('templatesOnPage', array_column($allTemplatesOnPage, 'uid') ?: [0])) {
if ($constantsHaveChanged) { $backendUser->pushModuleData($currentModuleIdentifier, $moduleData->toArray());
// Set the data to be saved
$recData = [];
$recData['sys_template'][$saveId]['constants'] = implode(LF, $this->templateService->raw);
// Create new tce-object
$tce = GeneralUtility::makeInstance(DataHandler::class);
$tce->start($recData, []);
$tce->process_datamap();
// re-read the template ...
// re-read the constants as they have changed
$this->initialize_editor($selectedTemplateRecord);
}
}
// Resetting the menu (stop)
$assigns['title'] = $this->linkWrapTemplateTitle($this->templateRow['title'], 'constants');
// Category and constant editor config
$availableCategories = $this->getCategoryLabels();
$currentCategory = (string)$this->moduleData->get('constant_editor_cat');
if (!empty($availableCategories)) {
$assigns['constantsMenu'] = BackendUtility::getDropdownMenu($this->id, 'constant_editor_cat', $currentCategory, $availableCategories, '', '', ['id' => 'constant_editor_cat']);
} }
$assigns['editorFields'] = $this->printFields($this->constants, $currentCategory);
$this->view->assignMultiple($assigns); $selectedTemplateRecord = (int)$moduleData->get('templatesOnPage');
return $this->view->renderResponse('ConstantEditor'); $templateRow = $this->parseTemplate($pageId, $selectedTemplateRecord);
if ($request->getParsedBody()['_savedok'] ?? false) {
// Update template with new data on save
$constantsHaveChanged = $this->templateService->ext_procesInput($request->getParsedBody(), $this->constants);
if ($constantsHaveChanged) {
$saveId = empty($templateRow['_ORIG_uid']) ? $templateRow['uid'] : $templateRow['_ORIG_uid'];
$recordData = [];
$recordData['sys_template'][$saveId]['constants'] = implode(LF, $this->templateService->raw);
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($recordData, []);
$dataHandler->process_datamap();
// Re-init template state as constants have changed
$this->parseTemplate($pageId, $selectedTemplateRecord);
} }
return $this->noTemplateAction(); }
$view = $this->moduleTemplateFactory->create($request);
$view->setTitle($languageService->sL($currentModule->getTitle()), $pageRecord['title']);
$view->getDocHeaderComponent()->setMetaInformation($pageRecord);
$this->addPreviewButtonToDocHeader($view, $pageId, (int)$pageRecord['doktype']);
$this->addShortcutButtonToDocHeader($view, $currentModuleIdentifier, $pageRecord, $pageId);
$this->addSaveButtonToDocHeader($view, $moduleData, $pageId);
$view->makeDocHeaderModuleMenu(['id' => $pageId]);
$availableCategories = $this->getCateg