diff --git a/Classes/Utility/TemplateDirectivesService.php b/Classes/Utility/TemplateDirectivesService.php index 45d78a1..b899d2c 100644 --- a/Classes/Utility/TemplateDirectivesService.php +++ b/Classes/Utility/TemplateDirectivesService.php @@ -30,9 +30,9 @@ class TemplateDirectivesService * ignoreUids=777,888 : list of uids to ignore * * @param string $lineFromConstant The constant line - * @return void + * @return true */ - public function lookForDirectives(string $lineFromConstant): void + public function lookForDirectives(string $lineFromConstant): bool { // Remove all spaces and line feed from input line $inputLine = trim(str_replace(' ', '', $lineFromConstant)); @@ -47,7 +47,9 @@ public function lookForDirectives(string $lineFromConstant): void $this->directives[$matchDirective[$i]] = $matchDirective[$i + 1]; } } + return true; } + return false; } /** diff --git a/Classes/Utility/TemplateService.php b/Classes/Utility/TemplateService.php index 0d90cf9..e45b6af 100644 --- a/Classes/Utility/TemplateService.php +++ b/Classes/Utility/TemplateService.php @@ -14,16 +14,26 @@ namespace Oktopuce\SiteGenerator\Utility; use Doctrine\DBAL\Exception; +use Oktopuce\SiteGenerator\Dto\BaseDto; +use Oktopuce\SiteGenerator\Wizard\Event\UpdateTyposcriptContentEvent; +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LogLevel; use TYPO3\CMS\Core\Database\Connection; 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\DataHandling\DataHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; // Cf. app/vendor/typo3/cms-tstemplate/Classes/Controller/ConstantEditorController.php class TemplateService { - public function __construct(private readonly ConnectionPool $connectionPool) + public function __construct( + private readonly EventDispatcherInterface $eventDispatcher, + private readonly ConnectionPool $connectionPool, + private readonly TemplateDirectivesService $templateDirectivesService, + private readonly DataHandler $dataHandler + ) { } @@ -79,8 +89,8 @@ public function calculateConstantPositions( $operatorPosition = strcspn($line, ' {=<'); $key = substr($line, 0, $operatorPosition); $line = ltrim(substr($line, $operatorPosition)); - if ($line[0] === '=') { - $constantPositions[$prefix . $key] = $lineCounter - 1; + if ($line[0] === '=' || $line[0] === ':') { + $constantPositions[$prefix . $key . "_" . $lineCounter] = $lineCounter - 1; } elseif ($line[0] === '{') { $braceLevel++; $this->calculateConstantPositions($rawTemplateConstantsArray, $constantPositions, $prefix . $key . '.', $braceLevel, $lineCounter); @@ -114,4 +124,154 @@ public function updateValueInConf(string $rawConstant, string $var): string } return implode('=', $parts); } + + /** + * @param string $table Name database table + * @param array $record Record data to update + * @param string $contentField Name of field which contains the content to update + * @param BaseDto $siteData + * @return bool true if content was update, false if no data was updated + */ + public function updateContent(string $table, array $record, string $contentField, BaseDto $siteData): bool + { + $rawTemplateConstantsArray = explode(LF, $record[$contentField] ?? ''); + $constantPositions = $this->calculateConstantPositions($rawTemplateConstantsArray); + + $updatedTemplateConstantsArray = []; + $directivesPositions = []; + + // For all constants, check if we need to update it + foreach ($constantPositions as $key => $rawP) { + // Looking for directives in comments + if($this->templateDirectivesService->lookForDirectives(($rawP > 0 ? $rawTemplateConstantsArray[$rawP - 1] : ''))) { + $directivesPositions[] = $rawP - 1; + }; + + $value = GeneralUtility::trimExplode('=', $rawTemplateConstantsArray[$rawP]); + + $uidsToExclude = GeneralUtility::trimExplode(',', + $this->templateDirectivesService->getIgnoreUids(), true); + $filteredMapping = $mapping = $siteData->getMappingArrayMerge( + $this->templateDirectivesService->getTable('pages') + ); + + // Manage uids to exclude + if (!empty($uidsToExclude)) { + $filteredMapping = array_filter($mapping, static function ($key) use ($uidsToExclude) { + return !in_array((string)$key, $uidsToExclude, true); + }, ARRAY_FILTER_USE_KEY); + } + + $action = $this->templateDirectivesService->getAction('mapInList'); + $updatedValue = ''; + + switch ($action) { + case 'mapInList' : + $updatedValue = $this->mapInList($value[1], $filteredMapping); + break; + case 'mapInString' : + $updatedValue = $this->mapInString($value[1], $filteredMapping); + break; + case 'exclude' : + // Exclude all line + break; + default : + // Call custom action if there is one + $parameters = $this->templateDirectivesService->getParameters(); + $event = $this->eventDispatcher->dispatch(new UpdateTyposcriptContentEvent($action, $parameters, + $value[1], $filteredMapping, $this->templateDirectivesService)); + $updatedValue = $event->getUpdatedValue(); + break; + } + + if (!empty($updatedValue)) { + $updatedTemplateConstantsArray[$rawP] = $updatedValue; + } + } + + if ($updatedTemplateConstantsArray) { + foreach ($updatedTemplateConstantsArray as $rowP => $updatedTemplateConstant) { + $rawTemplateConstantsArray[$rowP] = $this->updateValueInConf($rawTemplateConstantsArray[$rowP], $updatedTemplateConstant); + } + if($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['site_generator']['removeDirectivesFromOutput'] ?? true) { + // Remove directives from output + $rawTemplateConstantsArray = array_diff_key($rawTemplateConstantsArray, array_flip($directivesPositions)); + } + + // Set the data to be saved + $recordData = []; + $recordUid = $record['_ORIG_uid'] ?? $record['uid']; + $recordData[$table][$recordUid][$contentField] = implode(LF, $rawTemplateConstantsArray); + // Create new tce-object + $this->dataHandler->start($recordData, []); + $this->dataHandler->process_datamap(); + return true; + } + return false; + } + + /** + * Update constant in list + * + * @param string $value + * @param array $filteredMapping + * @return string Empty string or value updated + */ + protected function mapInList( + string $value, + array $filteredMapping + ): string { + $functionName = ''; + // Check if the value in constant is a list of int - 78,125,98 - or just an int + if (preg_match('/^\\s*([[:alpha:]]+)\\s*\\((.*)\\).*/', $value, $match)) { + $functionName = $match[1]; + $value = $match[2]; + } + if (preg_match('/^\d+(?:,\d+)*$/', $value)) { + $updateConstant = false; + + $listOfInt = GeneralUtility::trimExplode(',', $value, true); + + // Set new uid for constants + array_walk($listOfInt, + static function (&$constantValue) use ($filteredMapping, &$updateConstant) { + if (isset($filteredMapping[(int)$constantValue])) { + $constantValue = $filteredMapping[(int)$constantValue]; + $updateConstant = true; + } + }); + if ($updateConstant) { + $newList = implode(',', $listOfInt); + if($functionName) { + $newList = "$functionName($newList)"; + } + return $newList; + } + } + return (''); + } + + /** + * Update constants in string + * + * @param string $value + * @param array $filteredMapping + * @return string Empty string or value updated + */ + protected function mapInString( + string $value, + array $filteredMapping + ): string { + $updateConstant = false; + $count = 0; + + foreach ($filteredMapping as $modelUid => $siteUid) { + $value = str_replace((string)$modelUid, (string)$siteUid, $value, $count); + $updateConstant = ($updateConstant || $count > 0); + } + if ($updateConstant) { + return ($value); + } + return (''); + } } diff --git a/Classes/Wizard/Event/AbstractUpdateTyposcriptContentEvent.php b/Classes/Wizard/Event/AbstractUpdateTyposcriptContentEvent.php new file mode 100644 index 0000000..b5b68d3 --- /dev/null +++ b/Classes/Wizard/Event/AbstractUpdateTyposcriptContentEvent.php @@ -0,0 +1,130 @@ +action = $action; + $this->parameters = $parameters; + $this->value = $value; + $this->filteredMapping = $filteredMapping; + $this->templateDirectivesService = $templateDirectivesService; + } + + /** + * @return string + */ + public function getAction(): string + { + return $this->action; + } + + /** + * @return string + */ + public function getParameters(): string + { + return $this->parameters; + } + + /** + * @return string + */ + public function getValue(): string + { + return $this->value; + } + + /** + * @return array + */ + public function getFilteredMapping(): array + { + return $this->filteredMapping; + } + + /** + * @return TemplateDirectivesService + */ + public function getTemplateDirectivesService(): TemplateDirectivesService + { + return $this->templateDirectivesService; + } + + /** + * Get the updated value + * + * @return string Empty if no update required otherwise the value to update + */ + public function getUpdatedValue(): string + { + return $this->updatedValue; + } + + /** + * Set the updated value + * + * @param string $updatedValue The value to update + * @return void + */ + public function setUpdatedValue(string $updatedValue): void + { + $this->updatedValue = $updatedValue; + } +} diff --git a/Classes/Wizard/Event/UpdateTemplateHPEvent.php b/Classes/Wizard/Event/UpdateTemplateHPEvent.php index 625fe96..552f66d 100644 --- a/Classes/Wizard/Event/UpdateTemplateHPEvent.php +++ b/Classes/Wizard/Event/UpdateTemplateHPEvent.php @@ -13,118 +13,8 @@ namespace Oktopuce\SiteGenerator\Wizard\Event; -use Oktopuce\SiteGenerator\Utility\TemplateDirectivesService; - /** * This event is fired when state StateUpdateTemplateHP is executed and action is a custom action * i.e. : different from : mapInList, mapInString or exclude */ -final class UpdateTemplateHPEvent -{ - /** - * @var string - */ - private string $updatedValue = ''; - - /** - * @var string - */ - private string $action; - - /** - * @var string - */ - private string $parameters; - - /** - * @var array - */ - private array $filteredMapping; - - /** - * @var string - */ - private string $value; - - /** - * @var TemplateDirectivesService - */ - private TemplateDirectivesService $templateDirectivesService; - - /** - * @param string $action The action - * @param string $parameters The parameter - * @param string $value Current value - * @param array $filteredMapping Mapping filtered - i.e. ignoredUids already removed - * @param TemplateDirectivesService $templateDirectivesService - */ - public function __construct(string $action, string $parameters, string $value, array $filteredMapping, TemplateDirectivesService $templateDirectivesService) - { - $this->action = $action; - $this->parameters = $parameters; - $this->value = $value; - $this->filteredMapping = $filteredMapping; - $this->templateDirectivesService = $templateDirectivesService; - } - - /** - * @return string - */ - public function getAction(): string - { - return $this->action; - } - - /** - * @return string - */ - public function getParameters(): string - { - return $this->parameters; - } - - /** - * @return string - */ - public function getValue(): string - { - return $this->value; - } - - /** - * @return array - */ - public function getFilteredMapping(): array - { - return $this->filteredMapping; - } - - /** - * @return TemplateDirectivesService - */ - public function getTemplateDirectivesService(): TemplateDirectivesService - { - return $this->templateDirectivesService; - } - - /** - * Get the updated value - * - * @return string Empty if no update required otherwise the value to update - */ - public function getUpdatedValue(): string - { - return $this->updatedValue; - } - - /** - * Set the updated value - * - * @param string $updatedValue The value to update - * @return void - */ - public function setUpdatedValue(string $updatedValue): void - { - $this->updatedValue = $updatedValue; - } -} +final class UpdateTemplateHPEvent extends AbstractUpdateTyposcriptContentEvent {} diff --git a/Classes/Wizard/Event/UpdateTyposcriptContentEvent.php b/Classes/Wizard/Event/UpdateTyposcriptContentEvent.php new file mode 100644 index 0000000..dc31287 --- /dev/null +++ b/Classes/Wizard/Event/UpdateTyposcriptContentEvent.php @@ -0,0 +1,20 @@ +getMappingArrayMerge('pages') as $pageId) { @@ -58,6 +63,12 @@ protected function updatePageTS(BaseDto $siteData): void // Get record data $origRow = BackendUtility::getRecord('pages', $pageId); + if($this->templateService->updateContent('pages', $origRow, 'TSconfig', $siteData)) { + $pagesUpdatedOnIdentifiers[] = $pageId; + // Get record data + $origRow = BackendUtility::getRecord('pages', $pageId); + } + if ($origRow['TSconfig']) { $newsTSConfig = ''; $line = strtok($origRow['TSconfig'], PHP_EOL); @@ -100,5 +111,11 @@ protected function updatePageTS(BaseDto $siteData): void // @extensionScannerIgnoreLine $siteData->addMessage($this->translate('generate.success.updatePageTS', [implode(',', $pagesUpdated)])); } + + if (!empty($pagesUpdatedOnIdentifiers)) { + $this->log(LogLevel::NOTICE, 'Update page TSConfig : updating identifiers done for pages : ' . implode(',', $pagesUpdatedOnIdentifiers)); + // @extensionScannerIgnoreLine + $siteData->addMessage($this->translate('generate.success.updatePageTSonIdentifiers', [implode(',', $pagesUpdatedOnIdentifiers)])); + } } } diff --git a/Classes/Wizard/StateUpdateTemplateHP.php b/Classes/Wizard/StateUpdateTemplateHP.php index f9ba216..11a5c02 100644 --- a/Classes/Wizard/StateUpdateTemplateHP.php +++ b/Classes/Wizard/StateUpdateTemplateHP.php @@ -13,14 +13,9 @@ namespace Oktopuce\SiteGenerator\Wizard; -use Oktopuce\SiteGenerator\Utility\TemplateDirectivesService; -use Psr\EventDispatcher\EventDispatcherInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; use Psr\Log\LogLevel; -use TYPO3\CMS\Core\DataHandling\DataHandler; use Oktopuce\SiteGenerator\Dto\BaseDto; use Oktopuce\SiteGenerator\Utility\TemplateService; -use Oktopuce\SiteGenerator\Wizard\Event\UpdateTemplateHPEvent; /** * StateUpdateTemplate @@ -28,17 +23,12 @@ class StateUpdateTemplateHP extends StateBase implements SiteGeneratorStateInterface { /** - * @param TemplateDirectivesService $templateDirectivesService - * @param EventDispatcherInterface $eventDispatcher * @param TemplateService $templateService - * @param DataHandler $dataHandler */ public function __construct( - readonly protected TemplateDirectivesService $templateDirectivesService, - readonly protected EventDispatcherInterface $eventDispatcher, - readonly protected TemplateService $templateService, - readonly protected DataHandler $dataHandler - ) { + readonly protected TemplateService $templateService + ) + { parent::__construct(); } @@ -64,133 +54,12 @@ protected function updateTemplate(BaseDto $siteData): void { // Cf. app/vendor/typo3/cms-tstemplate/Classes/Controller/ConstantEditorController.php $allTemplatesOnPage = $this->templateService->getAllTemplateRecordsOnPage($siteData->getHpPid()); - foreach ($allTemplatesOnPage as $template) { - $rawTemplateConstantsArray = explode(LF, $template['constants']); - $constantPositions = $this->templateService->calculateConstantPositions($rawTemplateConstantsArray); - - $updatedTemplateConstantsArray = []; - - // For all constants, check if we need to update it - foreach ($constantPositions as $key => $rawP) { - // Looking for directives in comments - $this->templateDirectivesService->lookForDirectives(($rawP > 0 ? $rawTemplateConstantsArray[$rawP - 1] : '')); - $table = $this->templateDirectivesService->getTable('pages'); - - $value = GeneralUtility::trimExplode('=', $rawTemplateConstantsArray[$rawP]); - - $uidsToExclude = GeneralUtility::trimExplode(',', - $this->templateDirectivesService->getIgnoreUids(), true); - $filteredMapping = $mapping = $siteData->getMappingArrayMerge($table); - - // Manage uids to exclude - if (!empty($uidsToExclude)) { - $filteredMapping = array_filter($mapping, static function ($key) use ($uidsToExclude) { - return !in_array((string)$key, $uidsToExclude, true); - }, ARRAY_FILTER_USE_KEY); - } - - $action = $this->templateDirectivesService->getAction('mapInList'); - $updatedValue = ''; - - switch ($action) { - case 'mapInList' : - $updatedValue = $this->mapInList($value[1], $filteredMapping); - break; - case 'mapInString' : - $updatedValue = $this->mapInString($value[1], $filteredMapping); - break; - case 'exclude' : - // Exclude all line - break; - default : - // Call custom action if there is one - $parameters = $this->templateDirectivesService->getParameters(); - $event = $this->eventDispatcher->dispatch(new UpdateTemplateHPEvent($action, $parameters, - $value[1], $filteredMapping, $this->templateDirectivesService)); - $updatedValue = $event->getUpdatedValue(); - break; - } - - if (!empty($updatedValue)) { - $updatedTemplateConstantsArray[$rawP] = $updatedValue; - } - } - - if ($updatedTemplateConstantsArray) { - foreach ($updatedTemplateConstantsArray as $rowP => $updatedTemplateConstant) { - $rawTemplateConstantsArray[$rowP] = $this->templateService->updateValueInConf($rawTemplateConstantsArray[$rowP], $updatedTemplateConstant); - } - - // Set the data to be saved - $recordData = []; - $templateUid = $template['_ORIG_uid'] ?? $template['uid']; - $recordData['sys_template'][$templateUid]['constants'] = implode(LF, $rawTemplateConstantsArray); - // Create new tce-object - $this->dataHandler->start($recordData, []); - $this->dataHandler->process_datamap(); - + if($this->templateService->updateContent('sys_template', $template, 'constants', $siteData)) { // @extensionScannerIgnoreLine $siteData->addMessage($this->translate('generate.success.templateHpUpdated')); $this->log(LogLevel::NOTICE, 'Update home page template with new uids done'); - } - } - } - - /** - * Update constant in list - * - * @param string $value - * @param array $filteredMapping - * @return string Empty string or value updated - */ - protected function mapInList( - string $value, - array $filteredMapping - ): string { - // Check if the value in constant is a list of int - 78,125,98 - or just an int - if (preg_match('/^\d+(?:,\d+)*$/', $value)) { - $updateConstant = false; - - $listOfInt = GeneralUtility::trimExplode(',', $value, true); - - // Set new uid for constants - array_walk($listOfInt, - static function (&$constantValue) use ($filteredMapping, &$updateConstant) { - if (isset($filteredMapping[(int)$constantValue])) { - $constantValue = $filteredMapping[(int)$constantValue]; - $updateConstant = true; - } - }); - - if ($updateConstant) { - return implode(',', $listOfInt); - } - } - return (''); - } - - /** - * Update constants in string - * - * @param string $value - * @param array $filteredMapping - * @return string Empty string or value updated - */ - protected function mapInString( - string $value, - array $filteredMapping - ): string { - $updateConstant = false; - $count = 0; - - foreach ($filteredMapping as $modelUid => $siteUid) { - $value = str_replace((string)$modelUid, (string)$siteUid, $value, $count); - $updateConstant = ($updateConstant || $count > 0); - } - if ($updateConstant) { - return ($value); + }; } - return (''); } -} +} \ No newline at end of file diff --git a/Documentation/Administrators/UpdateTemplate/Index.rst b/Documentation/Administrators/UpdateTemplate/Index.rst index 4f1b13f..f4ec74b 100644 --- a/Documentation/Administrators/UpdateTemplate/Index.rst +++ b/Documentation/Administrators/UpdateTemplate/Index.rst @@ -35,7 +35,7 @@ Sample # Map all values in the string and exclude some of them # ext=SiteGenerator; table=tt_content; action=mapInString; ignoreUids=29,30 - multipleCeWithIgnore = addInList(28,29,30) + multipleCeWithIgnore := addInList(28,29,30) # For custom action you must used the event UpdateTemplateHPEvent # ext=SiteGenerator; action=customAction; parameters=custom parameters diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 59e0ef0..dd644d3 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -80,6 +80,9 @@ - Update page TSConfig (TCEMAIN.clearCacheCmd) done for pages : %s + + - Update page TSConfig on identifiers done for pages : %s + - The site configuration for domain '%s' has been created diff --git a/ext_conf_template.txt b/ext_conf_template.txt index b8cdb1a..ade3d15 100644 --- a/ext_conf_template.txt +++ b/ext_conf_template.txt @@ -8,6 +8,7 @@ # customsubcategory=140=Folder # customsubcategory=150=BE Group access lists # customsubcategory=160=Site configuration +# customsubcategory=170=TypoScript code update # cat=Basic/100/10_ModelPid; type=string; label=Models PID: Models page uid used to generate the new site (comma separated for multiple models) modelsPid = @@ -21,6 +22,10 @@ commonMountPointUid = # cat=Basic/120/10_OneForm; type=boolean; label=Only one form page for data: Check this if you want only one form page to gather data onlyOneFormPage = false +# cat=Basic/170/10_RemoveDirectivesFromOutput; type=boolean; label=Remove update directives from output: Check this if you don't want update directives in the output +removeDirectivesFromOutput = true + + # cat=Label/130/10_PageTitle; type=string; label=Home page title: The title used for the home page (can be overridden in form) homePageTitle = Home