From 1cbf71637f1cf8c15423a7004a7b5b9fdf5ac4f5 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 9 Feb 2024 17:21:19 +0100 Subject: [PATCH 01/20] remove version number --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index bc99678..6116d9e 100755 --- a/composer.json +++ b/composer.json @@ -1,7 +1,6 @@ { "name": "evoweb/ew-base", "type": "typo3-cms-extension", - "version": "1.0.0", "autoload": { "psr-4": { "Evoweb\\EwBase\\": "Classes/" From 9a9b4a7bcb9c5d9bf1c3103a0305014a418d6373 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 9 Feb 2024 17:32:09 +0100 Subject: [PATCH 02/20] Remove compatibility to TYPO3 11 --- Classes/Updates/GridelementsToContainerMigration.php | 5 ----- Classes/Updates/ParentChildToContainerMigration.php | 5 ----- composer.json | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/Classes/Updates/GridelementsToContainerMigration.php b/Classes/Updates/GridelementsToContainerMigration.php index fe6e1b0..9a205cb 100755 --- a/Classes/Updates/GridelementsToContainerMigration.php +++ b/Classes/Updates/GridelementsToContainerMigration.php @@ -34,11 +34,6 @@ class GridelementsToContainerMigration implements UpgradeWizardInterface $this->logger = $logManager->getLogger(self::class); } - public function getIdentifier(): string - { - return 'gridelementsToContainer'; - } - public function getTitle(): string { return 'Migrate gridelements to container'; diff --git a/Classes/Updates/ParentChildToContainerMigration.php b/Classes/Updates/ParentChildToContainerMigration.php index 7fe8ea2..685daab 100755 --- a/Classes/Updates/ParentChildToContainerMigration.php +++ b/Classes/Updates/ParentChildToContainerMigration.php @@ -34,11 +34,6 @@ class ParentChildToContainerMigration implements UpgradeWizardInterface $this->logger = $logManager->getLogger(self::class); } - public function getIdentifier(): string - { - return 'parentChildToContainer'; - } - public function getTitle(): string { return 'Migrate parent/child to container'; diff --git a/composer.json b/composer.json index 6116d9e..4aa554c 100755 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ } }, "require": { - "typo3/cms-core": "^11.5 || ^12.4 || dev-main", + "typo3/cms-core": "^12.4 || 12.4.x-dev || dev-main", "typo3/cms-backend": "*", "typo3/cms-extbase": "*", From f7c940a828574b8fdf45d1f2d0ce47e5caff1caf Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 28 Apr 2024 18:49:46 +0200 Subject: [PATCH 03/20] Make compatible with TYPO3 13.0 --- Classes/Command/ContentElementCommand.php | 4 +- .../Configuration/AdditionalConfiguration.php | 33 -- .../IsContentUsedOnPageLayout.php | 4 +- Classes/Form/Element/PickColorFromImage.php | 118 +++---- .../Form/Element/PickColorFromImagePre13.php | 304 ++++++++++++++++++ .../UsercentricsDatabaseEditRow.php | 28 +- .../UsercentricsTcaInline.php | 2 +- .../ParentChildToContainerMigration.php | 106 ------ .../Updates/ParentChildToContainerService.php | 168 ---------- Classes/User/AssetPath.php | 5 +- Classes/ViewHelpers/Array/AddViewHelper.php | 11 +- .../{Iterator => Array}/ExplodeViewHelper.php | 11 +- .../ViewHelpers/Be/ThumbnailViewHelper.php | 16 +- .../Condition/InArrayViewHelper.php | 11 +- .../Condition/StringContainsViewHelper.php | 13 +- .../Context/DevelopmentViewHelper.php | 6 +- .../Context/ProductionViewHelper.php | 6 +- .../ViewHelpers/Context/StagingViewHelper.php | 6 +- .../FixFlexformForExtbaseViewHelper.php | 77 ----- Classes/ViewHelpers/FlexFormViewHelper.php | 80 ----- Classes/ViewHelpers/HashViewHelper.php | 23 +- .../ViewHelpers/Iterator/AddViewHelper.php | 56 ---- Classes/ViewHelpers/PublicPathViewHelper.php | 20 +- Classes/ViewHelpers/ReplaceViewHelper.php | 10 +- Classes/ViewHelpers/SvgViewHelper.php | 17 +- Classes/ViewHelpers/TrimViewHelper.php | 17 +- Classes/Xclass/SiteDatabaseEditRow.php | 2 - Classes/Xclass/SiteTcaInline.php | 3 - ext_localconf.php | 6 +- 29 files changed, 432 insertions(+), 731 deletions(-) create mode 100644 Classes/Form/Element/PickColorFromImagePre13.php delete mode 100755 Classes/Updates/ParentChildToContainerMigration.php delete mode 100755 Classes/Updates/ParentChildToContainerService.php rename Classes/ViewHelpers/{Iterator => Array}/ExplodeViewHelper.php (94%) delete mode 100755 Classes/ViewHelpers/FixFlexformForExtbaseViewHelper.php delete mode 100755 Classes/ViewHelpers/FlexFormViewHelper.php delete mode 100755 Classes/ViewHelpers/Iterator/AddViewHelper.php diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php index 687e76d..488b022 100755 --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -36,10 +36,10 @@ class ContentElementCommand extends Command parent::__construct(); } - protected function configure() + protected function configure(): void { $this - ->setAliases(['kc-sitepackage']) + ->setAliases(['ce']) ->addOption( 'pageId', '-p', diff --git a/Classes/Configuration/AdditionalConfiguration.php b/Classes/Configuration/AdditionalConfiguration.php index 298a17a..69530c8 100755 --- a/Classes/Configuration/AdditionalConfiguration.php +++ b/Classes/Configuration/AdditionalConfiguration.php @@ -12,8 +12,6 @@ namespace Evoweb\EwBase\Configuration; * of the License, or any later version. */ -use Mfc\OAuth2\ResourceServer\GitLab; -use Mfc\OAuth2\ResourceServer\Registry; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -22,24 +20,6 @@ class AdditionalConfiguration { protected string $extensionKey = 'ew_base'; - protected array $oauthOptions = [ - 'enabled' => true, // Enable/Disable the provider - 'arguments' => [ - 'appId' => '', - 'appSecret' => '', - 'projectName' => '', - 'gitlabServer' => 'https://github.com', - // User level at which the user will be given admin permissions - 'gitlabAdminUserLevel' => 30, - // Groups to assign to the User (comma separated list possible) - 'gitlabDefaultGroups' => 1, - // UserConfig db and/or file mount from groups - 'gitlabUserOption' => 3, - // Blocks users with flag external from access the backend - 'blockExternalUser' => false, - ], - ]; - protected array $developConfig = [ 'BE' => [ 'debug' => true, @@ -169,19 +149,6 @@ class AdditionalConfiguration } } - public function addGitlabLogin(array $options = []): void - { - if (!empty($options)) { - $this->oauthOptions['arguments']['gitlabAdminUserLevel'] = GitLab::USER_LEVEL_DEVELOPER; - Registry::addServer( - 'gitlab', - 'Login mit GitLab', - GitLab::class, - $this->arrayMergeRecursive($this->oauthOptions, $options) - ); - } - } - protected function arrayMergeRecursive(array $array1, array $array2): array { $merged = $array1; diff --git a/Classes/EventListener/IsContentUsedOnPageLayout.php b/Classes/EventListener/IsContentUsedOnPageLayout.php index daafc1e..dbf6175 100755 --- a/Classes/EventListener/IsContentUsedOnPageLayout.php +++ b/Classes/EventListener/IsContentUsedOnPageLayout.php @@ -2,7 +2,7 @@ namespace Evoweb\EwBase\EventListener; -use TYPO3\CMS\Backend\ViEvoweb\Event\IsContentUsedOnPageLayoutEvent; +use TYPO3\CMS\Backend\View\Event\IsContentUsedOnPageLayoutEvent; class IsContentUsedOnPageLayout { @@ -15,7 +15,7 @@ class IsContentUsedOnPageLayout { $found = false; foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { - if (strpos($cType, $search) !== false) { + if (str_contains($cType, $search)) { $found = true; break; } diff --git a/Classes/Form/Element/PickColorFromImage.php b/Classes/Form/Element/PickColorFromImage.php index afe6a18..e9e2e4e 100755 --- a/Classes/Form/Element/PickColorFromImage.php +++ b/Classes/Form/Element/PickColorFromImage.php @@ -15,8 +15,12 @@ namespace Evoweb\EwBase\Form\Element; * LICENSE.txt file that was distributed with this source code. */ +use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Backend\Form\Element\AbstractFormElement; -use TYPO3\CMS\Backend\Form\NodeFactory; +use TYPO3\CMS\Backend\Form\Event\ModifyImageManipulationPreviewUrlEvent; +use TYPO3\CMS\Backend\Routing\UriBuilder; +use TYPO3\CMS\Backend\View\BackendViewFactory; +use TYPO3\CMS\Core\Crypto\HashService; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException; @@ -27,15 +31,13 @@ use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Core\Utility\StringUtility; -use TYPO3Fluid\Fluid\ViEvoweb\TemplateView; -use TYPO3Fluid\Fluid\ViEvoweb\ViewInterface; class PickColorFromImage extends AbstractFormElement { + private string $wizardRouteName = 'ajax_wizard_image_manipulation'; + /** * Default element configuration - * - * @var array */ protected static array $defaultConfig = [ 'imageField' => 'image', @@ -110,53 +112,31 @@ class PickColorFromImage extends AbstractFormElement ], ]; - protected ViewInterface $templateView; - - public function __construct(NodeFactory $nodeFactory, array $data) - { - parent::__construct($nodeFactory, $data); - // Would be great, if we could inject the view here, but since the constructor is in the interface, we can't - // @todo: It's unfortunate we're using Typo3Fluid TemplateView directly here. We can't - // inject BackendViewFactory here since __construct() is polluted by NodeInterface. - // Remove __construct() from NodeInterface to have DI, then use BackendViewFactory here. - $view = GeneralUtility::makeInstance(TemplateView::class); - $templatePaths = $view->getRenderingContext()->getTemplatePaths(); - $templatePaths - ->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Templates')]); - $templatePaths - ->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Partials')]); - $this->templateView = $view; - } + public function __construct( + private readonly BackendViewFactory $backendViewFactory, + private readonly UriBuilder $uriBuilder, + private readonly EventDispatcherInterface $eventDispatcher, + private readonly ResourceFactory $resourceFactory, + private readonly HashService $hashService, + ) {} + /** + * @throws InvalidConfigurationException + */ public function render(): array { $resultArray = $this->initializeResultArray(); $parameterArray = $this->data['parameterArray']; $config = $this->populateConfiguration($parameterArray['fieldConf']['config']); - $fieldId = StringUtility::getUniqueId('formengine-color-'); - $file = $this->getFileObject($this->data['databaseRow'], $config['imageField']); + $file = $this->getFile($this->data['databaseRow'], $config['imageField']); if (!$file) { - $languageService = $this->getLanguageService(); - $label = $languageService->sL( - 'LLL:EXT:ew_base/Resources/Private/Language/locallang_db.xlf:imageField.empty' - ); - $fieldLabel = $this->data['processedTca']['columns'][$config['imageField']]['label']; - $resultArray['html'] = ' -
-
-
- - ' . sprintf($label, '' . $fieldLabel . '') . ' - -
-
-
- '; // Early return in case we do not find a file return $resultArray; } + $config = $this->processConfiguration($config, $parameterArray['itemFormElValue'], $file); + $fieldInformationResult = $this->renderFieldInformation(); $fieldInformationHtml = $fieldInformationResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); @@ -169,14 +149,6 @@ class PickColorFromImage extends AbstractFormElement $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); - $width = $this->formMaxWidth( - MathUtility::forceIntegerInRange( - $config['size'] ?? $this->defaultInputWidth, - $this->minimumInputWidth, - $this->maxInputWidth - ) - ); - $arguments = [ 'fieldInformation' => $fieldInformationHtml, 'fieldControl' => $fieldControlHtml, @@ -195,10 +167,15 @@ class PickColorFromImage extends AbstractFormElement 'validation' => '[]', ], 'config' => $config, - 'width' => $width, + 'wizardUri' => $this->getWizardUri(), + 'wizardPayload' => json_encode($this->getWizardPayload($config['cropVariants'], $file)), + 'previewUrl' => $this->eventDispatcher->dispatch( + new ModifyImageManipulationPreviewUrlEvent($this->data['databaseRow'], $config, $file) + )->getPreviewUrl(), ]; if ($arguments['isAllowedFileExtension']) { + $fieldId = StringUtility::getUniqueId('formengine-color-'); $resultArray['stylesheetFiles'][] = 'EXT:ew_base/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css'; $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create( @@ -214,14 +191,14 @@ class PickColorFromImage extends AbstractFormElement $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); } } - - $this->templateView->assignMultiple($arguments); - $resultArray['html'] = $this->templateView->render('Form/ImageManipulationElement'); + $view = $this->backendViewFactory->create($this->data['request']); + $view->assignMultiple($arguments); + $resultArray['html'] = $view->render('Form/ImageManipulationElement'); return $resultArray; } - protected function getFileObject(array $row, string $fieldName): ?File + protected function getFile(array $row, string $fieldName): ?File { $file = null; $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null; @@ -230,21 +207,26 @@ class PickColorFromImage extends AbstractFormElement } if (MathUtility::canBeInterpretedAsInteger($fileUid)) { try { - $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); - $fileReference = $resourceFactory->getFileReferenceObject($fileUid); - $file = $fileReference->getOriginalFile(); + $file = $this->resourceFactory->getFileObject($fileUid); } catch (FileDoesNotExistException | \InvalidArgumentException) { } } return $file; } + /** + * @throws InvalidConfigurationException + */ protected function populateConfiguration(array $baseConfiguration): array { $defaultConfig = self::$defaultConfig; + + // If ratios are set do not add default options + if (isset($baseConfiguration['cropVariants'])) { + unset($defaultConfig['cropVariants']); + } + $config = array_replace_recursive($defaultConfig, $baseConfiguration); - $imageConfig = $this->data['processedTca']['columns'][$config['imageField']]; - $config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants']; if (!is_array($config['cropVariants'])) { throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267); @@ -282,8 +264,8 @@ class PickColorFromImage extends AbstractFormElement $config['cropVariants'] = $cropVariants; + // By default, we allow all image extensions that can be handled by the GFX functionality $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; - return $config; } @@ -301,4 +283,22 @@ class PickColorFromImage extends AbstractFormElement ); return $config; } + + protected function getWizardUri(): string + { + return (string)$this->uriBuilder->buildUriFromRoute($this->wizardRouteName); + } + + protected function getWizardPayload(array $cropVariants, File $image): array + { + $uriArguments = []; + $arguments = [ + 'cropVariants' => $cropVariants, + 'image' => $image->getUid(), + ]; + $uriArguments['arguments'] = json_encode($arguments); + $uriArguments['signature'] = $this->hashService->hmac((string)($uriArguments['arguments']), $this->wizardRouteName); + + return $uriArguments; + } } diff --git a/Classes/Form/Element/PickColorFromImagePre13.php b/Classes/Form/Element/PickColorFromImagePre13.php new file mode 100644 index 0000000..ac81de5 --- /dev/null +++ b/Classes/Form/Element/PickColorFromImagePre13.php @@ -0,0 +1,304 @@ + 'image', + 'cropVariants' => [ + 'default' => [ + 'title' => + 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.default', + 'allowedAspectRatios' => [ + '16:9' => [ + 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9', + 'value' => 16 / 9, + ], + '3:2' => [ + 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.3_2', + 'value' => 3 / 2, + ], + '4:3' => [ + 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3', + 'value' => 4 / 3, + ], + '1:1' => [ + 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1', + 'value' => 1.0, + ], + 'NaN' => [ + 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free', + 'value' => 0.0, + ], + ], + 'selectedRatio' => 'NaN', + 'cropArea' => [ + 'x' => 0.0, + 'y' => 0.0, + 'width' => 1.0, + 'height' => 1.0, + ], + ], + ], + ]; + + /** + * Default field information enabled for this element. + * + * @var array + */ + protected $defaultFieldInformation = [ + 'tcaDescription' => [ + 'renderType' => 'tcaDescription', + ], + ]; + + /** + * Default field wizards enabled for this element. + * + * @var array + */ + protected $defaultFieldWizard = [ + 'localizationStateSelector' => [ + 'renderType' => 'localizationStateSelector', + ], + 'otherLanguageContent' => [ + 'renderType' => 'otherLanguageContent', + 'after' => [ + 'localizationStateSelector', + ], + ], + 'defaultLanguageDifferences' => [ + 'renderType' => 'defaultLanguageDifferences', + 'after' => [ + 'otherLanguageContent', + ], + ], + ]; + + protected ViewInterface $templateView; + + public function __construct(NodeFactory $nodeFactory, array $data) + { + parent::__construct($nodeFactory, $data); + // Would be great, if we could inject the view here, but since the constructor is in the interface, we can't + // @todo: It's unfortunate we're using Typo3Fluid TemplateView directly here. We can't + // inject BackendViewFactory here since __construct() is polluted by NodeInterface. + // Remove __construct() from NodeInterface to have DI, then use BackendViewFactory here. + $view = GeneralUtility::makeInstance(TemplateView::class); + $templatePaths = $view->getRenderingContext()->getTemplatePaths(); + $templatePaths + ->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Templates')]); + $templatePaths + ->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Partials')]); + $this->templateView = $view; + } + + public function render(): array + { + $resultArray = $this->initializeResultArray(); + $parameterArray = $this->data['parameterArray']; + $config = $this->populateConfiguration($parameterArray['fieldConf']['config']); + $fieldId = StringUtility::getUniqueId('formengine-color-'); + + $file = $this->getFileObject($this->data['databaseRow'], $config['imageField']); + if (!$file) { + $languageService = $this->getLanguageService(); + $label = $languageService->sL( + 'LLL:EXT:ew_base/Resources/Private/Language/locallang_db.xlf:imageField.empty' + ); + $fieldLabel = $this->data['processedTca']['columns'][$config['imageField']]['label']; + $resultArray['html'] = ' +
+
+
+ + ' . sprintf($label, '' . $fieldLabel . '') . ' + +
+
+
+ '; + // Early return in case we do not find a file + return $resultArray; + } + + $fieldInformationResult = $this->renderFieldInformation(); + $fieldInformationHtml = $fieldInformationResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); + + $fieldControlResult = $this->renderFieldControl(); + $fieldControlHtml = $fieldControlResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false); + + $fieldWizardResult = $this->renderFieldWizard(); + $fieldWizardHtml = $fieldWizardResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); + + $width = $this->formMaxWidth( + MathUtility::forceIntegerInRange( + $config['size'] ?? $this->defaultInputWidth, + $this->minimumInputWidth, + $this->maxInputWidth + ) + ); + + $arguments = [ + 'fieldInformation' => $fieldInformationHtml, + 'fieldControl' => $fieldControlHtml, + 'fieldWizard' => $fieldWizardHtml, + 'isAllowedFileExtension' => in_array( + strtolower($file->getExtension()), + GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), + true + ), + 'image' => $file, + 'formEngine' => [ + 'field' => [ + 'value' => $parameterArray['itemFormElValue'], + 'name' => $parameterArray['itemFormElName'], + ], + 'validation' => '[]', + ], + 'config' => $config, + 'width' => $width, + ]; + + if ($arguments['isAllowedFileExtension']) { + $resultArray['stylesheetFiles'][] = + 'EXT:ew_base/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css'; + $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create( + '@evoweb/ew-base/form-engine/element/pick-color-from-image.js', + 'PickColorFromImage' + )->instance( + $fieldId, + $parameterArray['itemFormElValue'], + ($parameterArray['fieldConf']['config']['readOnly'] ?? false) + ); + $arguments['formEngine']['field']['id'] = $fieldId; + if ($config['required'] ?? false) { + $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); + } + } + + $this->templateView->assignMultiple($arguments); + $resultArray['html'] = $this->templateView->render('Form/ImageManipulationElement'); + + return $resultArray; + } + + protected function getFileObject(array $row, string $fieldName): ?File + { + $file = null; + $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null; + if (is_array($fileUid) && isset($fileUid[0]['uid'])) { + $fileUid = $fileUid[0]['uid']; + } + if (MathUtility::canBeInterpretedAsInteger($fileUid)) { + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $fileReference = $resourceFactory->getFileReferenceObject($fileUid); + $file = $fileReference->getOriginalFile(); + } catch (FileDoesNotExistException | \InvalidArgumentException) { + } + } + return $file; + } + + protected function populateConfiguration(array $baseConfiguration): array + { + $defaultConfig = self::$defaultConfig; + $config = array_replace_recursive($defaultConfig, $baseConfiguration); + $imageConfig = $this->data['processedTca']['columns'][$config['imageField']]; + $config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants']; + + if (!is_array($config['cropVariants'])) { + throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267); + } + + $cropVariants = []; + foreach ($config['cropVariants'] as $id => $cropVariant) { + // Filter allowed aspect ratios + $cropVariant['allowedAspectRatios'] = array_filter( + $cropVariant['allowedAspectRatios'] ?? [], + static function ($aspectRatio) { + return !(bool)($aspectRatio['disabled'] ?? false); + } + ); + + // Ignore disabled crop variants + if (!empty($cropVariant['disabled'])) { + continue; + } + + if (empty($cropVariant['allowedAspectRatios'])) { + throw new InvalidConfigurationException( + 'Crop variants configuration ' . $id . ' contains no allowed aspect ratios', + 1620147893 + ); + } + + // Enforce a crop area (default is full image) + if (empty($cropVariant['cropArea'])) { + $cropVariant['cropArea'] = Area::createEmpty()->asArray(); + } + + $cropVariants[$id] = $cropVariant; + } + + $config['cropVariants'] = $cropVariants; + + $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; + + return $config; + } + + protected function processConfiguration(array $config, string &$elementValue, File $file): array + { + $cropVariantCollection = CropVariantCollection::create($elementValue, $config['cropVariants']); + if (empty($config['readOnly']) && !empty($file->getProperty('width'))) { + $cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file); + $elementValue = (string)$cropVariantCollection; + } + $config['cropVariants'] = $cropVariantCollection->asArray(); + $config['allowedExtensions'] = implode( + ', ', + GeneralUtility::trimExplode(',', $config['allowedExtensions'], true) + ); + return $config; + } +} diff --git a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php index 1d7e571..2c0b3f2 100755 --- a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php +++ b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php @@ -14,8 +14,6 @@ class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow /** * First level of ['customData']['siteData'] to ['databaseRow'] * - * @param array $result - * @return array * @throws \RuntimeException */ public function addData(array $result): array @@ -24,23 +22,19 @@ class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow return $result; } - $tableName = $result['tableName']; $siteFinder = GeneralUtility::makeInstance(SiteFinder::class, $this->siteConfiguration); - if (in_array($tableName, ['site_usercentrics'], true)) { - $rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']); - try { - $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); - $parentFieldName = $result['inlineParentFieldName']; - if (!isset($rowData[$parentFieldName])) { - throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092); - } - $rowData = $rowData[$parentFieldName][$result['vanillaUid']]; - $result['databaseRow']['uid'] = $result['vanillaUid']; - } catch (SiteNotFoundException $e) { - $rowData = []; + + $rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']); + try { + $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); + $parentFieldName = $result['inlineParentFieldName']; + if (!isset($rowData[$parentFieldName])) { + throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092); } - } else { - return $result; + $rowData = $rowData[$parentFieldName][$result['vanillaUid']]; + $result['databaseRow']['uid'] = $result['vanillaUid']; + } catch (SiteNotFoundException) { + $rowData = []; } foreach ($rowData as $fieldName => $value) { diff --git a/Classes/Form/FormDataProvider/UsercentricsTcaInline.php b/Classes/Form/FormDataProvider/UsercentricsTcaInline.php index 1d8e59e..e8685fd 100755 --- a/Classes/Form/FormDataProvider/UsercentricsTcaInline.php +++ b/Classes/Form/FormDataProvider/UsercentricsTcaInline.php @@ -40,7 +40,7 @@ class UsercentricsTcaInline extends SiteTcaInline continue; } $childTableName = $fieldConfig['config']['foreign_table'] ?? ''; - if (!in_array($childTableName, ['site_usercentrics'], true)) { + if ($childTableName !== 'site_usercentrics') { continue; } $result['processedTca']['columns'][$fieldName]['children'] = []; diff --git a/Classes/Updates/ParentChildToContainerMigration.php b/Classes/Updates/ParentChildToContainerMigration.php deleted file mode 100755 index 685daab..0000000 --- a/Classes/Updates/ParentChildToContainerMigration.php +++ /dev/null @@ -1,106 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - public function getTitle(): string - { - return 'Migrate parent/child to container'; - } - - public function getDescription(): string - { - return 'Migrates content elements that are type parent/child to corresponding container elements'; - } - - public function getPrerequisites(): array - { - return [ - DatabaseUpdatedPrerequisite::class - ]; - } - - public function updateNecessary(): bool - { - return $this->hasRecordsToUpdate(); - } - - public function executeUpdate(): bool - { - $updateSuccessful = true; - try { - $this->service->migrate(); - } catch (\Exception $exception) { - $this->logger->log(LogLevel::ERROR, $exception->getMessage()); - $updateSuccessful = false; - } - - return $updateSuccessful; - } - - protected function hasRecordsToUpdate(): bool - { - $queryBuilder = $this->getPreparedQueryBuilder(); - $tableColumns = $queryBuilder - ->getConnection() - ->createSchemaManager() - ->listTableColumns(self::TABLE_NAME); - return isset($tableColumns['parent']) - && $queryBuilder - ->count('uid') - ->executeQuery() - ->fetchOne() > 0; - } - - protected function getPreparedQueryBuilder(): QueryBuilder - { - $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - $queryBuilder - ->from(self::TABLE_NAME) - ->where( - // check if there are still records that have parent instead of tx_container_parent - $queryBuilder->expr()->gt('parent', 0) - ); - return $queryBuilder; - } - - protected function getConnectionPool(): ConnectionPool - { - return GeneralUtility::makeInstance(ConnectionPool::class); - } -} diff --git a/Classes/Updates/ParentChildToContainerService.php b/Classes/Updates/ParentChildToContainerService.php deleted file mode 100755 index 737ed75..0000000 --- a/Classes/Updates/ParentChildToContainerService.php +++ /dev/null @@ -1,168 +0,0 @@ - [ 'CType' => 'container-downloads' ], -]; -*/ - -class ParentChildToContainerService -{ - private const TABLE_NAME = 'tt_content'; - - protected int $colPosOffset = 0; - - protected array $configuration = []; - - public function __construct( - protected ConnectionPool $connectionPool, - protected DataHandler $dataHandler, - protected FlexFormService $flexFormService, - ) { - $config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; - $this->colPosOffset = $config['childParentColPosOffset'] ?? 0; - $this->configuration = $config['childParentMigrationMapping'] ?? []; - } - - public function migrate(): void - { - $this->initializeDataHandler(); - $this->migrateConfiguredContainer(); - } - - protected function initializeDataHandler(): void - { - $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); - $backendUser->user = [ - 'uid' => 0, - 'admin' => 1, - ]; - $this->dataHandler->start([], [], $backendUser); - } - - - protected function migrateConfiguredContainer(): void - { - array_walk($this->configuration, function($config, $key) { - $parents = $this->getParentsByCType((string)$key); - foreach ($parents as $parent) { - $this->processParentMigration($parent, $config); - } - }); - } - - protected function processParentMigration(array $parent, array $config): void - { - $children = $this->getChildren($parent['uid']); - foreach ($children as $child) { - $this->processChildMigration($child, $parent); - } - - $this->updateElement( - $parent['uid'], - [ - 'CType' => $this->getCType($parent, $config), - 'children' => 0, - ] - ); - } - - protected function processChildMigration(array $child, array $parent): void - { - $this->updateElement( - $child['uid'], - [ - 'tx_container_parent' => $parent['uid'], - 'colPos' => $child['colPos'] + $this->colPosOffset, - 'parent' => 0, - ] - ); - } - - protected function getCType(array $container, array $config): string - { - if (is_array($config['CType'])) { - $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); - $result = $config['CType']['matches'][$value] ?? null; - } else { - $result = $config['CType'] ?? null; - } - - if (empty($result)) { - throw new \Exception('CType must always be set'); - } - - return $result; - } - - - protected function updateElement(int $uid, array $changes): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); - } - - protected function getChildren(int $parentUid): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - - return $queryBuilder - ->select('*') - ->where( - $queryBuilder->expr()->eq( - 'parent', - $queryBuilder->createNamedParameter($parentUid, \PDO::PARAM_INT) - ) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getParentsByCType(string $cType): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - $expr = $queryBuilder->expr(); - - return $queryBuilder - ->select('*') - ->where( - $expr->eq('CType', $queryBuilder->createNamedParameter($cType)) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($table); - - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - - $queryBuilder->from($table); - - return $queryBuilder; - } -} diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php index 9659830..45b94f4 100755 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Evoweb\EwBase\User; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; /** @@ -18,8 +17,6 @@ class AssetPath { public function getAbsolutePublicPath(string $content, array $conf): string { - return $content - . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') - . ltrim(PathUtility::getPublicResourceWebPath($conf['file']), '/'); + return PathUtility::getAbsoluteWebPath($conf['file']); } } diff --git a/Classes/ViewHelpers/Array/AddViewHelper.php b/Classes/ViewHelpers/Array/AddViewHelper.php index 1d0b5c9..7bdc507 100755 --- a/Classes/ViewHelpers/Array/AddViewHelper.php +++ b/Classes/ViewHelpers/Array/AddViewHelper.php @@ -26,7 +26,7 @@ class AddViewHelper extends AbstractViewHelper */ protected $escapeOutput = false; - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('array', 'array', 'Array to add value to'); @@ -34,18 +34,11 @@ class AddViewHelper extends AbstractViewHelper $this->registerArgument('value', 'mixed', 'Value to add'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): array { $array = $arguments['array'] ?: []; $key = $arguments['key']; $value = !is_null($arguments['value']) ? $arguments['value'] : $renderChildrenClosure(); diff --git a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php b/Classes/ViewHelpers/Array/ExplodeViewHelper.php similarity index 94% rename from Classes/ViewHelpers/Iterator/ExplodeViewHelper.php rename to Classes/ViewHelpers/Array/ExplodeViewHelper.php index e50139f..364d39e 100755 --- a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php +++ b/Classes/ViewHelpers/Array/ExplodeViewHelper.php @@ -1,6 +1,6 @@ registerArgument( 'as', @@ -48,16 +48,11 @@ class ExplodeViewHelper extends AbstractViewHelper ); } - /** - * Render method - * - * @return string|array - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): mixed { $content = $arguments['content'] ?? $renderChildrenClosure(); $glue = static::resolveGlue($arguments); $content = call_user_func_array(static::$method, [$glue, $content]); diff --git a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php index f229195..b2ffa84 100755 --- a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php +++ b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php @@ -30,10 +30,7 @@ class ThumbnailViewHelper extends AbstractViewHelper { use CompileWithRenderStatic; - /** - * Initializes the arguments - */ - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('row', 'array', 'content data', true); @@ -41,20 +38,11 @@ class ThumbnailViewHelper extends AbstractViewHelper $this->registerArgument('fieldName', 'string', 'field name', true); } - /** - * Render a constant - * - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return string Value of constant - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): string { $row = $arguments['row']; $tableName = $arguments['tableName']; $fieldName = $arguments['fieldName']; diff --git a/Classes/ViewHelpers/Condition/InArrayViewHelper.php b/Classes/ViewHelpers/Condition/InArrayViewHelper.php index bc713f5..d59e8f6 100755 --- a/Classes/ViewHelpers/Condition/InArrayViewHelper.php +++ b/Classes/ViewHelpers/Condition/InArrayViewHelper.php @@ -19,21 +19,14 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; */ class InArrayViewHelper extends AbstractConditionViewHelper { - /** - * Initialize arguments - */ - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('haystack', 'array', 'haystack', true); $this->registerArgument('needle', 'mixed', 'needle', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + protected static function evaluateCondition($arguments = null): bool { $array = $arguments['haystack']->toArray(); return in_array($arguments['needle'], $array); diff --git a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php index efb6b6f..70f01c4 100755 --- a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php +++ b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php @@ -19,22 +19,15 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; */ class StringContainsViewHelper extends AbstractConditionViewHelper { - /** - * Initialize arguments - */ - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('haystack', 'string', 'haystack', true); $this->registerArgument('needle', 'string', 'need', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + protected static function evaluateCondition($arguments = null): bool { - return false !== strpos($arguments['haystack'], $arguments['needle']); + return str_contains($arguments['haystack'], $arguments['needle']); } } diff --git a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php index 0875df1..b9a93fd 100755 --- a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php +++ b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php @@ -14,11 +14,7 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; class DevelopmentViewHelper extends AbstractConditionViewHelper { - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + protected static function evaluateCondition($arguments = null): bool { return Environment::getContext()->isDevelopment(); } diff --git a/Classes/ViewHelpers/Context/ProductionViewHelper.php b/Classes/ViewHelpers/Context/ProductionViewHelper.php index f5356bb..9736b03 100755 --- a/Classes/ViewHelpers/Context/ProductionViewHelper.php +++ b/Classes/ViewHelpers/Context/ProductionViewHelper.php @@ -14,11 +14,7 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; class ProductionViewHelper extends AbstractConditionViewHelper { - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + protected static function evaluateCondition($arguments = null): bool { return Environment::getContext()->isProduction(); } diff --git a/Classes/ViewHelpers/Context/StagingViewHelper.php b/Classes/ViewHelpers/Context/StagingViewHelper.php index a76003e..c26f387 100755 --- a/Classes/ViewHelpers/Context/StagingViewHelper.php +++ b/Classes/ViewHelpers/Context/StagingViewHelper.php @@ -14,11 +14,7 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper; class StagingViewHelper extends AbstractConditionViewHelper { - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + protected static function evaluateCondition($arguments = null): bool { return 'Production/Staging' === (string)Environment::getContext(); } diff --git a/Classes/ViewHelpers/FixFlexformForExtbaseViewHelper.php b/Classes/ViewHelpers/FixFlexformForExtbaseViewHelper.php deleted file mode 100755 index de070d9..0000000 --- a/Classes/ViewHelpers/FixFlexformForExtbaseViewHelper.php +++ /dev/null @@ -1,77 +0,0 @@ - - * .... - * - * - * fixed flexform data for extbase controller - * - * - * @api - */ -class FixFlexformForExtbaseViewHelper extends AbstractViewHelper -{ - use CompileWithRenderStatic; - - /** - * @var boolean - */ - protected $escapeOutput = false; - - public function initializeArguments(): void - { - parent::initializeArguments(); - $this->registerArgument('data', 'array', 'The data array of content element', true); - } - - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $templateVariableContainer = $renderingContext->getVariableProvider(); - - /** @var FlexFormTools $flexFormTools */ - $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); - - $data = $arguments['data']; - if (is_array($data['pi_flexform'])) { - $data['pi_flexform'] = $flexFormTools->flexArray2Xml($data['pi_flexform']); - } - - $templateVariableContainer->add('data', $data); - $output = $renderChildrenClosure(); - $templateVariableContainer->remove('data'); - - return $output; - } -} diff --git a/Classes/ViewHelpers/FlexFormViewHelper.php b/Classes/ViewHelpers/FlexFormViewHelper.php deleted file mode 100755 index cd7fc27..0000000 --- a/Classes/ViewHelpers/FlexFormViewHelper.php +++ /dev/null @@ -1,80 +0,0 @@ - - * .... - * - * - * fixed flexform data for extbase controller - * - * - * @api - */ -class FlexFormViewHelper extends AbstractViewHelper -{ - use CompileWithRenderStatic; - - /** - * @var boolean - */ - protected $escapeOutput = false; - - public function initializeArguments() - { - parent::initializeArguments(); - $this->registerArgument('data', 'array', 'Array to get flex form data from', true); - $this->registerArgument('fieldName', 'string', 'Field name', false, 'pi_flexform'); - $this->registerArgument('as', 'string', 'Name of the variable to assign', false, 'flexFormData'); - } - - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $data = []; - - if (is_array($arguments['data'])) { - if (isset($arguments['data'][$arguments['fieldName']])) { - $data = is_array($arguments['data'][$arguments['fieldName']]) ? - $arguments['data'][$arguments['fieldName']] : - GeneralUtility::xml2array($arguments['data'][$arguments['fieldName']]); - $data = $data['data'] ?? $data; - } - } - - $templateVariableContainer = $renderingContext->getVariableProvider(); - $templateVariableContainer->add($arguments['as'], $data); - $content = $renderChildrenClosure(); - $templateVariableContainer->remove($arguments['as']); - - return $content; - } -} diff --git a/Classes/ViewHelpers/HashViewHelper.php b/Classes/ViewHelpers/HashViewHelper.php index 0b61138..6eb11fc 100755 --- a/Classes/ViewHelpers/HashViewHelper.php +++ b/Classes/ViewHelpers/HashViewHelper.php @@ -13,6 +13,9 @@ namespace Evoweb\EwBase\ViewHelpers; * LICENSE.txt file that was distributed with this source code. */ +use TYPO3\CMS\Core\Crypto\HashService; +use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; @@ -26,33 +29,31 @@ class HashViewHelper extends AbstractViewHelper */ protected $escapeOutput = false; - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('action', 'string', 'Target action'); $this->registerArgument('arguments', 'array', 'Arguments for the controller action, associative array'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return string - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): string { $result = ''; if ( $arguments['action'] !== null && $arguments['arguments'] !== null && isset($arguments['arguments']['user']) ) { - $result = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac( - $arguments['action'] . '::' . $arguments['arguments']['user'] - ); + if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 13) { + $result = GeneralUtility::hmac($arguments['action'] . '::' . $arguments['arguments']['user']); + } else { + /** @var HashService $hashService */ + $hashService = GeneralUtility::makeInstance(HashService::class); + $result = $hashService->hmac($arguments['action'] . '::' . $arguments['arguments']['user'], ''); + } } return $result; diff --git a/Classes/ViewHelpers/Iterator/AddViewHelper.php b/Classes/ViewHelpers/Iterator/AddViewHelper.php deleted file mode 100755 index bb24422..0000000 --- a/Classes/ViewHelpers/Iterator/AddViewHelper.php +++ /dev/null @@ -1,56 +0,0 @@ -registerArgument('array', 'array', 'Array to add value to'); - $this->registerArgument('key', 'string', 'Key to add value by', true); - $this->registerArgument('value', 'mixed', 'Value to add'); - } - - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $array = $arguments['array'] ?: []; - $key = $arguments['key']; - $value = $arguments['value'] ?: $renderChildrenClosure(); - - return array_merge($array, [$key => $value]); - } -} diff --git a/Classes/ViewHelpers/PublicPathViewHelper.php b/Classes/ViewHelpers/PublicPathViewHelper.php index 769f001..2971755 100755 --- a/Classes/ViewHelpers/PublicPathViewHelper.php +++ b/Classes/ViewHelpers/PublicPathViewHelper.php @@ -41,27 +41,23 @@ class PublicPathViewHelper extends AbstractViewHelper */ protected $escapeOutput = false; - protected static ?array $frontendGroupIds = null; - - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('path', 'string', 'Extension resource path', true); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return bool - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): string { $path = (string)$arguments['path']; - return PathUtility::getPublicResourceWebPath($path); + try { + $path = PathUtility::getPublicResourceWebPath($path); + } catch (\Exception) { + $path = ''; + } + return $path; } } diff --git a/Classes/ViewHelpers/ReplaceViewHelper.php b/Classes/ViewHelpers/ReplaceViewHelper.php index 77a9837..02d4807 100755 --- a/Classes/ViewHelpers/ReplaceViewHelper.php +++ b/Classes/ViewHelpers/ReplaceViewHelper.php @@ -44,24 +44,18 @@ class ReplaceViewHelper extends AbstractViewHelper { use CompileWithContentArgumentAndRenderStatic; - public function initializeArguments() + public function initializeArguments(): void { $this->registerArgument('value', 'string', 'String to replace in'); $this->registerArgument('search', 'string', 'Search string'); $this->registerArgument('replace', 'string', 'Replace value'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return null - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): string { $content = $arguments['value']; if ($content === null) { $content = $renderChildrenClosure(); diff --git a/Classes/ViewHelpers/SvgViewHelper.php b/Classes/ViewHelpers/SvgViewHelper.php index 2290936..b3e5c76 100755 --- a/Classes/ViewHelpers/SvgViewHelper.php +++ b/Classes/ViewHelpers/SvgViewHelper.php @@ -18,6 +18,7 @@ use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Type\Icon\IconState; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; /** @@ -61,7 +62,7 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; * * */ -class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class SvgViewHelper extends AbstractViewHelper { use CompileWithRenderStatic; @@ -76,24 +77,16 @@ class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper { $this->registerArgument('identifier', 'string', 'Identifier of the icon as registered in the Icon Registry.', true); $this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, Icon::SIZE_SMALL); - $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false); + $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false, ''); $this->registerArgument('state', 'string', 'Sets the state of the icon. All values of the Icons.states enum are allowed, these are: "default" and "disabled".', false, IconState::STATE_DEFAULT); - $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false); + $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false, ''); } - /** - * Prints icon html for $identifier key - * - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return string - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { + ): Icon { $identifier = $arguments['identifier']; $size = $arguments['size']; $overlay = $arguments['overlay']; diff --git a/Classes/ViewHelpers/TrimViewHelper.php b/Classes/ViewHelpers/TrimViewHelper.php index 68683ff..033ee8d 100755 --- a/Classes/ViewHelpers/TrimViewHelper.php +++ b/Classes/ViewHelpers/TrimViewHelper.php @@ -26,27 +26,20 @@ class TrimViewHelper extends AbstractViewHelper */ protected $escapeOutput = false; - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('content', 'string', 'Content to be trimmed'); - $this->registerArgument('characters', 'string', 'Characters to be removed', false); + $this->registerArgument('characters', 'string', 'Characters to be removed'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return string - */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ) { - $content = $arguments['content'] ? $arguments['content'] : $renderChildrenClosure(); - $characters = $arguments['characters'] ? $arguments['characters'] : null; + ): string { + $content = $arguments['content'] ?: $renderChildrenClosure(); + $characters = $arguments['characters'] ?: null; if ($characters !== null) { $content = trim($content, $characters); diff --git a/Classes/Xclass/SiteDatabaseEditRow.php b/Classes/Xclass/SiteDatabaseEditRow.php index cc4a4c5..93fae38 100755 --- a/Classes/Xclass/SiteDatabaseEditRow.php +++ b/Classes/Xclass/SiteDatabaseEditRow.php @@ -12,8 +12,6 @@ class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow /** * First level of ['customData']['siteData'] to ['databaseRow'] * - * @param array $result - * @return array * @throws \RuntimeException */ public function addData(array $result): array diff --git a/Classes/Xclass/SiteTcaInline.php b/Classes/Xclass/SiteTcaInline.php index 548f6f3..1119f87 100755 --- a/Classes/Xclass/SiteTcaInline.php +++ b/Classes/Xclass/SiteTcaInline.php @@ -8,9 +8,6 @@ class SiteTcaInline extends BaseSiteTcaInline { /** * Resolve inline fields - * - * @param array $result - * @return array */ public function addData(array $result): array { diff --git a/ext_localconf.php b/ext_localconf.php index 1f96207..9b40451 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -3,6 +3,7 @@ defined('TYPO3') or die('access denied'); use Evoweb\EwBase\Form\Element\PickColorFromImage; +use Evoweb\EwBase\Form\Element\PickColorFromImagePre13; use Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow; use Evoweb\EwBase\Form\FormDataProvider\UsercentricsTcaInline; use Evoweb\EwBase\Hooks\UsercentricsPostRenderHook; @@ -12,6 +13,8 @@ use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseParentPageRow; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline as BaseSiteTcaInline; use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage; +use TYPO3\CMS\Core\Information\Typo3Version; +use TYPO3\CMS\Core\Utility\GeneralUtility; call_user_func(function () { $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['ewb'] = [ 'Evoweb\\EwBase\\ViewHelpers' ]; @@ -19,10 +22,11 @@ call_user_func(function () { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'][] = UsercentricsPostRenderHook::class . '->executePostRenderHook'; + $versionPre13 = (GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 13; $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [ 'nodeName' => 'pick-color-from-image', 'priority' => '70', - 'class' => PickColorFromImage::class, + 'class' => $versionPre13 ? PickColorFromImagePre13::class : PickColorFromImage::class, ]; $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteDatabaseEditRow::class] = [ From f45e6b5c3b4d1304ffea575d2e7993540faba837 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 5 May 2024 19:36:12 +0200 Subject: [PATCH 04/20] Remove unused data processor --- Classes/DataProcessing/ContainerProcessor.php | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100755 Classes/DataProcessing/ContainerProcessor.php diff --git a/Classes/DataProcessing/ContainerProcessor.php b/Classes/DataProcessing/ContainerProcessor.php deleted file mode 100755 index 6d48d2a..0000000 --- a/Classes/DataProcessing/ContainerProcessor.php +++ /dev/null @@ -1,58 +0,0 @@ -getChildrenByColPos($colPos); - - if (!$processorConfiguration['doNotProcessChildren']) { - $contentRecordRenderer = new RecordsContentObject(); - $conf = [ - 'tables' => 'tt_content' - ]; - foreach ($children as &$child) { - if ($child['l18n_parent'] > 0) { - $conf['source'] = $child['l18n_parent']; - } else { - $conf['source'] = $child['uid']; - } - if ($child['t3ver_oid'] > 0) { - $conf['source'] = $child['t3ver_oid']; - } - $child['renderedContent'] = $cObj->render($contentRecordRenderer, $conf); - /** @var ContentObjectRenderer $recordContentObjectRenderer */ - $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $recordContentObjectRenderer->start($child, 'tt_content'); - $child = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $child); - } - } - - $processedData[$as] = $children; - return $processedData; - } -} From 6cd429c808957f853965a65e7438c7f33610b56e Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 5 May 2024 20:05:32 +0200 Subject: [PATCH 05/20] Limit to 13.x --- .../Form/Element/PickColorFromImagePre13.php | 304 ------------------ composer.json | 2 +- ext_localconf.php | 4 +- 3 files changed, 2 insertions(+), 308 deletions(-) delete mode 100644 Classes/Form/Element/PickColorFromImagePre13.php diff --git a/Classes/Form/Element/PickColorFromImagePre13.php b/Classes/Form/Element/PickColorFromImagePre13.php deleted file mode 100644 index ac81de5..0000000 --- a/Classes/Form/Element/PickColorFromImagePre13.php +++ /dev/null @@ -1,304 +0,0 @@ - 'image', - 'cropVariants' => [ - 'default' => [ - 'title' => - 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.default', - 'allowedAspectRatios' => [ - '16:9' => [ - 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9', - 'value' => 16 / 9, - ], - '3:2' => [ - 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.3_2', - 'value' => 3 / 2, - ], - '4:3' => [ - 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3', - 'value' => 4 / 3, - ], - '1:1' => [ - 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1', - 'value' => 1.0, - ], - 'NaN' => [ - 'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free', - 'value' => 0.0, - ], - ], - 'selectedRatio' => 'NaN', - 'cropArea' => [ - 'x' => 0.0, - 'y' => 0.0, - 'width' => 1.0, - 'height' => 1.0, - ], - ], - ], - ]; - - /** - * Default field information enabled for this element. - * - * @var array - */ - protected $defaultFieldInformation = [ - 'tcaDescription' => [ - 'renderType' => 'tcaDescription', - ], - ]; - - /** - * Default field wizards enabled for this element. - * - * @var array - */ - protected $defaultFieldWizard = [ - 'localizationStateSelector' => [ - 'renderType' => 'localizationStateSelector', - ], - 'otherLanguageContent' => [ - 'renderType' => 'otherLanguageContent', - 'after' => [ - 'localizationStateSelector', - ], - ], - 'defaultLanguageDifferences' => [ - 'renderType' => 'defaultLanguageDifferences', - 'after' => [ - 'otherLanguageContent', - ], - ], - ]; - - protected ViewInterface $templateView; - - public function __construct(NodeFactory $nodeFactory, array $data) - { - parent::__construct($nodeFactory, $data); - // Would be great, if we could inject the view here, but since the constructor is in the interface, we can't - // @todo: It's unfortunate we're using Typo3Fluid TemplateView directly here. We can't - // inject BackendViewFactory here since __construct() is polluted by NodeInterface. - // Remove __construct() from NodeInterface to have DI, then use BackendViewFactory here. - $view = GeneralUtility::makeInstance(TemplateView::class); - $templatePaths = $view->getRenderingContext()->getTemplatePaths(); - $templatePaths - ->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Templates')]); - $templatePaths - ->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Partials')]); - $this->templateView = $view; - } - - public function render(): array - { - $resultArray = $this->initializeResultArray(); - $parameterArray = $this->data['parameterArray']; - $config = $this->populateConfiguration($parameterArray['fieldConf']['config']); - $fieldId = StringUtility::getUniqueId('formengine-color-'); - - $file = $this->getFileObject($this->data['databaseRow'], $config['imageField']); - if (!$file) { - $languageService = $this->getLanguageService(); - $label = $languageService->sL( - 'LLL:EXT:ew_base/Resources/Private/Language/locallang_db.xlf:imageField.empty' - ); - $fieldLabel = $this->data['processedTca']['columns'][$config['imageField']]['label']; - $resultArray['html'] = ' -
-
-
- - ' . sprintf($label, '' . $fieldLabel . '') . ' - -
-
-
- '; - // Early return in case we do not find a file - return $resultArray; - } - - $fieldInformationResult = $this->renderFieldInformation(); - $fieldInformationHtml = $fieldInformationResult['html']; - $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); - - $fieldControlResult = $this->renderFieldControl(); - $fieldControlHtml = $fieldControlResult['html']; - $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false); - - $fieldWizardResult = $this->renderFieldWizard(); - $fieldWizardHtml = $fieldWizardResult['html']; - $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); - - $width = $this->formMaxWidth( - MathUtility::forceIntegerInRange( - $config['size'] ?? $this->defaultInputWidth, - $this->minimumInputWidth, - $this->maxInputWidth - ) - ); - - $arguments = [ - 'fieldInformation' => $fieldInformationHtml, - 'fieldControl' => $fieldControlHtml, - 'fieldWizard' => $fieldWizardHtml, - 'isAllowedFileExtension' => in_array( - strtolower($file->getExtension()), - GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), - true - ), - 'image' => $file, - 'formEngine' => [ - 'field' => [ - 'value' => $parameterArray['itemFormElValue'], - 'name' => $parameterArray['itemFormElName'], - ], - 'validation' => '[]', - ], - 'config' => $config, - 'width' => $width, - ]; - - if ($arguments['isAllowedFileExtension']) { - $resultArray['stylesheetFiles'][] = - 'EXT:ew_base/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css'; - $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create( - '@evoweb/ew-base/form-engine/element/pick-color-from-image.js', - 'PickColorFromImage' - )->instance( - $fieldId, - $parameterArray['itemFormElValue'], - ($parameterArray['fieldConf']['config']['readOnly'] ?? false) - ); - $arguments['formEngine']['field']['id'] = $fieldId; - if ($config['required'] ?? false) { - $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); - } - } - - $this->templateView->assignMultiple($arguments); - $resultArray['html'] = $this->templateView->render('Form/ImageManipulationElement'); - - return $resultArray; - } - - protected function getFileObject(array $row, string $fieldName): ?File - { - $file = null; - $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null; - if (is_array($fileUid) && isset($fileUid[0]['uid'])) { - $fileUid = $fileUid[0]['uid']; - } - if (MathUtility::canBeInterpretedAsInteger($fileUid)) { - try { - $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); - $fileReference = $resourceFactory->getFileReferenceObject($fileUid); - $file = $fileReference->getOriginalFile(); - } catch (FileDoesNotExistException | \InvalidArgumentException) { - } - } - return $file; - } - - protected function populateConfiguration(array $baseConfiguration): array - { - $defaultConfig = self::$defaultConfig; - $config = array_replace_recursive($defaultConfig, $baseConfiguration); - $imageConfig = $this->data['processedTca']['columns'][$config['imageField']]; - $config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants']; - - if (!is_array($config['cropVariants'])) { - throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267); - } - - $cropVariants = []; - foreach ($config['cropVariants'] as $id => $cropVariant) { - // Filter allowed aspect ratios - $cropVariant['allowedAspectRatios'] = array_filter( - $cropVariant['allowedAspectRatios'] ?? [], - static function ($aspectRatio) { - return !(bool)($aspectRatio['disabled'] ?? false); - } - ); - - // Ignore disabled crop variants - if (!empty($cropVariant['disabled'])) { - continue; - } - - if (empty($cropVariant['allowedAspectRatios'])) { - throw new InvalidConfigurationException( - 'Crop variants configuration ' . $id . ' contains no allowed aspect ratios', - 1620147893 - ); - } - - // Enforce a crop area (default is full image) - if (empty($cropVariant['cropArea'])) { - $cropVariant['cropArea'] = Area::createEmpty()->asArray(); - } - - $cropVariants[$id] = $cropVariant; - } - - $config['cropVariants'] = $cropVariants; - - $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; - - return $config; - } - - protected function processConfiguration(array $config, string &$elementValue, File $file): array - { - $cropVariantCollection = CropVariantCollection::create($elementValue, $config['cropVariants']); - if (empty($config['readOnly']) && !empty($file->getProperty('width'))) { - $cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file); - $elementValue = (string)$cropVariantCollection; - } - $config['cropVariants'] = $cropVariantCollection->asArray(); - $config['allowedExtensions'] = implode( - ', ', - GeneralUtility::trimExplode(',', $config['allowedExtensions'], true) - ); - return $config; - } -} diff --git a/composer.json b/composer.json index 4aa554c..ecedfc6 100755 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ } }, "require": { - "typo3/cms-core": "^12.4 || 12.4.x-dev || dev-main", + "typo3/cms-core": "^13.0 || 13.0.x-dev || dev-main", "typo3/cms-backend": "*", "typo3/cms-extbase": "*", diff --git a/ext_localconf.php b/ext_localconf.php index 9b40451..5fd1db4 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -3,7 +3,6 @@ defined('TYPO3') or die('access denied'); use Evoweb\EwBase\Form\Element\PickColorFromImage; -use Evoweb\EwBase\Form\Element\PickColorFromImagePre13; use Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow; use Evoweb\EwBase\Form\FormDataProvider\UsercentricsTcaInline; use Evoweb\EwBase\Hooks\UsercentricsPostRenderHook; @@ -22,11 +21,10 @@ call_user_func(function () { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'][] = UsercentricsPostRenderHook::class . '->executePostRenderHook'; - $versionPre13 = (GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 13; $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [ 'nodeName' => 'pick-color-from-image', 'priority' => '70', - 'class' => $versionPre13 ? PickColorFromImagePre13::class : PickColorFromImage::class, + 'class' => PickColorFromImage::class, ]; $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteDatabaseEditRow::class] = [ From 3a7abf9a73e4a875e6f76170d6ea59839d2b5d1a Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 10 May 2024 19:43:56 +0200 Subject: [PATCH 06/20] Fix code quality tools annotations --- Classes/Command/ContentElementCommand.php | 5 +- .../IsContentUsedOnPageLayout.php | 25 ---------- Classes/Form/Element/PickColorFromImage.php | 5 +- Classes/Hooks/PageLayoutView.php | 34 -------------- Classes/Hooks/UsercentricsPostRenderHook.php | 46 +++++++++++-------- .../GridelementsToContainerService.php | 3 +- Classes/User/AssetPath.php | 2 +- .../ViewHelpers/Array/ExplodeViewHelper.php | 25 +++++----- Classes/ViewHelpers/HashViewHelper.php | 11 ++--- Classes/ViewHelpers/SvgViewHelper.php | 46 ++++++++++++++++--- Classes/Xclass/SiteDatabaseEditRow.php | 2 +- Configuration/Services.yaml | 3 -- Configuration/Sets/EwBase/config.yaml | 2 + .../Sets/EwBase/settings.definitions.yaml | 33 +++++++++++++ Configuration/Sets/EwBase/settings.yaml | 22 +++++++++ ext_localconf.php | 22 --------- 16 files changed, 144 insertions(+), 142 deletions(-) delete mode 100755 Classes/EventListener/IsContentUsedOnPageLayout.php delete mode 100755 Classes/Hooks/PageLayoutView.php create mode 100644 Configuration/Sets/EwBase/config.yaml create mode 100644 Configuration/Sets/EwBase/settings.definitions.yaml create mode 100644 Configuration/Sets/EwBase/settings.yaml diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php index 488b022..d59a5c8 100755 --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -28,11 +28,8 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; class ContentElementCommand extends Command { - protected ConnectionPool $connectionPool; - - public function __construct(ConnectionPool $connectionPool) + public function __construct(protected ConnectionPool $connectionPool) { - $this->connectionPool = $connectionPool; parent::__construct(); } diff --git a/Classes/EventListener/IsContentUsedOnPageLayout.php b/Classes/EventListener/IsContentUsedOnPageLayout.php deleted file mode 100755 index dbf6175..0000000 --- a/Classes/EventListener/IsContentUsedOnPageLayout.php +++ /dev/null @@ -1,25 +0,0 @@ -setUsed($event->isRecordUsed() || $this->findCTypeBegin($event->getRecord()['CType'])); - } - - public function findCTypeBegin($cType): bool - { - $found = false; - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { - if (str_contains($cType, $search)) { - $found = true; - break; - } - } - return $found; - } -} diff --git a/Classes/Form/Element/PickColorFromImage.php b/Classes/Form/Element/PickColorFromImage.php index e9e2e4e..98c5df9 100755 --- a/Classes/Form/Element/PickColorFromImage.php +++ b/Classes/Form/Element/PickColorFromImage.php @@ -120,9 +120,6 @@ class PickColorFromImage extends AbstractFormElement private readonly HashService $hashService, ) {} - /** - * @throws InvalidConfigurationException - */ public function render(): array { $resultArray = $this->initializeResultArray(); @@ -238,7 +235,7 @@ class PickColorFromImage extends AbstractFormElement $cropVariant['allowedAspectRatios'] = array_filter( $cropVariant['allowedAspectRatios'] ?? [], static function ($aspectRatio) { - return !(bool)($aspectRatio['disabled'] ?? false); + return !($aspectRatio['disabled'] ?? false); } ); diff --git a/Classes/Hooks/PageLayoutView.php b/Classes/Hooks/PageLayoutView.php deleted file mode 100755 index 608ccf1..0000000 --- a/Classes/Hooks/PageLayoutView.php +++ /dev/null @@ -1,34 +0,0 @@ -findCTypeBegin($params['record']['CType']); - } - - public function findCTypeBegin($cType): bool - { - $found = false; - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { - if (str_contains($cType, $search)) { - $found = true; - break; - } - } - return $found; - } -} diff --git a/Classes/Hooks/UsercentricsPostRenderHook.php b/Classes/Hooks/UsercentricsPostRenderHook.php index adf37f0..da1304e 100755 --- a/Classes/Hooks/UsercentricsPostRenderHook.php +++ b/Classes/Hooks/UsercentricsPostRenderHook.php @@ -15,10 +15,9 @@ namespace Evoweb\EwBase\Hooks; use Exception; use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Core\Environment; +use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Site\SiteFinder; -use TYPO3\CMS\Core\Utility\GeneralUtility; class UsercentricsPostRenderHook { @@ -28,6 +27,12 @@ class UsercentricsPostRenderHook 'useBlocker' => false, ]; + protected ?Site $site = null; + + public function __construct(protected SiteFinder $siteFinder) + { + } + public function executePostRenderHook(array $params): void { $pageUid = $this->getRequest()->getAttribute('frontend.controller')->page['uid'] ?? 0; @@ -38,16 +43,16 @@ class UsercentricsPostRenderHook $siteArguments = $this->getCurrentSiteArguments($pageUid); $id = $siteArguments['id'] ?? ''; if ($id !== '') { - $extensionConfig = $this->getExtensionConfiguration(); + $extensionConfig = $this->getExtensionConfiguration($pageUid); $preload = []; - $usercentrics = []; + $userCentrics = []; // CMP 1, 2, ... $version = $siteArguments['version'] ?? ''; if ($config = $extensionConfig[$version] ?? false) { $preload[] = $config['preload'] ?? ''; - $usercentrics[] = $config['template'] ?? ''; + $userCentrics[] = $config['template'] ?? ''; } // Blocker @@ -56,10 +61,10 @@ class UsercentricsPostRenderHook && $config = $extensionConfig['block'] ?? false ) { $preload[] = $config['preload'] ?? ''; - $usercentrics[] = $config['template'] ?? ''; + $userCentrics[] = $config['template'] ?? ''; } - $params['titleTag'] .= str_replace('###ID###', $id, implode(LF, array_merge($preload, $usercentrics))); + $params['titleTag'] .= str_replace('###ID###', $id, implode(LF, array_merge($preload, $userCentrics))); } } @@ -67,10 +72,8 @@ class UsercentricsPostRenderHook { $siteArguments = $this->siteArguments; + $site = $this->getSiteByPageUid($pageUid); try { - /** @var SiteFinder $siteFinder */ - $siteFinder = GeneralUtility::makeInstance(SiteFinder::class); - $site = $siteFinder->getSiteByPageId($pageUid); foreach ($site->getAttribute('usercentrics') as $usercentrics) { if (str_contains($usercentrics['applicationContext'] ?? '', (string)Environment::getContext())) { $siteArguments = $usercentrics; @@ -78,21 +81,28 @@ class UsercentricsPostRenderHook } } } catch (Exception) { + $siteArguments = []; } return is_array($siteArguments) ? $siteArguments : []; } - protected function getExtensionConfiguration(): array + protected function getExtensionConfiguration($pageUid): array { - /** @var ExtensionConfiguration $extensionConfiguration */ - $extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class); - try { - $configuration = $extensionConfiguration->get('ew_base', 'userCentrics'); - } catch (Exception) { - $configuration = []; + $siteSettings = $this->getSiteByPageUid($pageUid)->getSettings(); + $configuration = $siteSettings->has('ew-base') ? $siteSettings->get('ew-base') : []; + return is_array($configuration['userCentrics'] ?? false) ? $configuration['userCentrics'] : []; + } + + protected function getSiteByPageUid(int $pageUid): Site + { + if (!$this->site) { + try { + $this->site = $this->siteFinder->getSiteByPageId($pageUid); + } catch (Exception) { + } } - return is_array($configuration) ? $configuration : []; + return $this->site; } protected function getRequest(): ServerRequestInterface diff --git a/Classes/Updates/GridelementsToContainerService.php b/Classes/Updates/GridelementsToContainerService.php index 4b0cdac..7a8f70d 100755 --- a/Classes/Updates/GridelementsToContainerService.php +++ b/Classes/Updates/GridelementsToContainerService.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Evoweb\EwBase\Updates; +use Doctrine\DBAL\ParameterType; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\Query\QueryBuilder; @@ -227,7 +228,7 @@ class GridelementsToContainerService ->where( $queryBuilder->expr()->eq( 'tx_gridelements_container', - $queryBuilder->createNamedParameter($containerUid, \PDO::PARAM_INT) + $queryBuilder->createNamedParameter($containerUid, ParameterType::INTEGER) ) ) ->orderBy('sorting') diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php index 45b94f4..4b047ba 100755 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -17,6 +17,6 @@ class AssetPath { public function getAbsolutePublicPath(string $content, array $conf): string { - return PathUtility::getAbsoluteWebPath($conf['file']); + return $content . PathUtility::getAbsoluteWebPath($conf['file']); } } diff --git a/Classes/ViewHelpers/Array/ExplodeViewHelper.php b/Classes/ViewHelpers/Array/ExplodeViewHelper.php index 364d39e..1757334 100755 --- a/Classes/ViewHelpers/Array/ExplodeViewHelper.php +++ b/Classes/ViewHelpers/Array/ExplodeViewHelper.php @@ -35,14 +35,14 @@ class ExplodeViewHelper extends AbstractViewHelper $this->registerArgument( 'as', 'string', - 'Template variable name to assign; if not specified the ViewHelper returns the variable instead.' + 'Template variable name to assign; if not specified the ViewHelper returns the variable instead.', ); $this->registerArgument('content', 'string', 'String to be exploded by glue'); $this->registerArgument( 'glue', 'string', - 'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" ' . - '(fx "constant:LF" for linefeed as glue)', + 'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" ' + . '(fx "constant:LF" for linefeed as glue)', false, ',' ); @@ -58,13 +58,13 @@ class ExplodeViewHelper extends AbstractViewHelper $content = call_user_func_array(static::$method, [$glue, $content]); $as = $arguments['as']; - if (true === empty($as)) { - $output = $content; - } else { + if ($as !== null) { $templateVariableContainer = $renderingContext->getVariableProvider(); $templateVariableContainer->add($as, $content); $output = $renderChildrenClosure(); $templateVariableContainer->remove($as); + } else { + $output = $content; } return $output; } @@ -72,16 +72,13 @@ class ExplodeViewHelper extends AbstractViewHelper protected static function resolveGlue(array $arguments): string { $glue = $arguments['glue']; - if (false !== strpos($glue, ':') && 1 < strlen($glue)) { + if (str_contains($glue, ':') && 1 < strlen($glue)) { // glue contains a special type identifier, resolve the actual glue list ($type, $value) = explode(':', $glue); - switch ($type) { - case 'constant': - $glue = constant($value); - break; - default: - $glue = $value; - } + $glue = match ($type) { + 'constant' => constant($value), + default => $value, + }; } return $glue; } diff --git a/Classes/ViewHelpers/HashViewHelper.php b/Classes/ViewHelpers/HashViewHelper.php index 6eb11fc..ebd997c 100755 --- a/Classes/ViewHelpers/HashViewHelper.php +++ b/Classes/ViewHelpers/HashViewHelper.php @@ -14,7 +14,6 @@ namespace Evoweb\EwBase\ViewHelpers; */ use TYPO3\CMS\Core\Crypto\HashService; -use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; @@ -47,13 +46,9 @@ class HashViewHelper extends AbstractViewHelper && $arguments['arguments'] !== null && isset($arguments['arguments']['user']) ) { - if ((GeneralUtility::makeInstance(Typo3Version::class))->getMajorVersion() < 13) { - $result = GeneralUtility::hmac($arguments['action'] . '::' . $arguments['arguments']['user']); - } else { - /** @var HashService $hashService */ - $hashService = GeneralUtility::makeInstance(HashService::class); - $result = $hashService->hmac($arguments['action'] . '::' . $arguments['arguments']['user'], ''); - } + /** @var HashService $hashService */ + $hashService = GeneralUtility::makeInstance(HashService::class); + $result = $hashService->hmac($arguments['action'] . '::' . $arguments['arguments']['user'], ''); } return $result; diff --git a/Classes/ViewHelpers/SvgViewHelper.php b/Classes/ViewHelpers/SvgViewHelper.php index b3e5c76..2281ae1 100755 --- a/Classes/ViewHelpers/SvgViewHelper.php +++ b/Classes/ViewHelpers/SvgViewHelper.php @@ -15,7 +15,8 @@ namespace Evoweb\EwBase\ViewHelpers; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\IconFactory; -use TYPO3\CMS\Core\Type\Icon\IconState; +use TYPO3\CMS\Core\Imaging\IconSize; +use TYPO3\CMS\Core\Imaging\IconState; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; @@ -75,11 +76,42 @@ class SvgViewHelper extends AbstractViewHelper public function initializeArguments(): void { - $this->registerArgument('identifier', 'string', 'Identifier of the icon as registered in the Icon Registry.', true); - $this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, Icon::SIZE_SMALL); - $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false, ''); - $this->registerArgument('state', 'string', 'Sets the state of the icon. All values of the Icons.states enum are allowed, these are: "default" and "disabled".', false, IconState::STATE_DEFAULT); - $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false, ''); + $this->registerArgument( + 'identifier', + 'string', + 'Identifier of the icon as registered in the Icon Registry.', + true + ); + $this->registerArgument( + 'size', + 'string', + 'Desired size of the icon. All values of the Icons.sizes enum are allowed,' + . ' these are: "small", "default", "large" and "overlay".', + false, + IconSize::SMALL + ); + $this->registerArgument( + 'overlay', + 'string', + 'Identifier of an overlay icon as registered in the Icon Registry.', + false, + '' + ); + $this->registerArgument( + 'state', + 'string', + 'Sets the state of the icon. All values of the Icons.states enum are allowed,' + . ' these are: "default" and "disabled".', + false, + IconState::STATE_DEFAULT + ); + $this->registerArgument( + 'alternativeMarkupIdentifier', + 'string', + 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', + false, + '' + ); } public static function renderStatic( @@ -90,7 +122,7 @@ class SvgViewHelper extends AbstractViewHelper $identifier = $arguments['identifier']; $size = $arguments['size']; $overlay = $arguments['overlay']; - $state = IconState::cast($arguments['state']); + $state = IconState::tryFrom($arguments['state']); $alternativeMarkupIdentifier = $arguments['alternativeMarkupIdentifier']; $iconFactory = GeneralUtility::makeInstance(IconFactory::class); return $iconFactory->getIcon($identifier, $size, $overlay, $state)->getMarkup($alternativeMarkupIdentifier); diff --git a/Classes/Xclass/SiteDatabaseEditRow.php b/Classes/Xclass/SiteDatabaseEditRow.php index 93fae38..7b184ba 100755 --- a/Classes/Xclass/SiteDatabaseEditRow.php +++ b/Classes/Xclass/SiteDatabaseEditRow.php @@ -37,7 +37,7 @@ class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow } $rowData = $rowData[$parentFieldName][$result['vanillaUid']]; $result['databaseRow']['uid'] = $result['vanillaUid']; - } catch (SiteNotFoundException $e) { + } catch (SiteNotFoundException) { $rowData = []; } } else { diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 8050c5b..7654eb3 100755 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -20,9 +20,6 @@ services: Evoweb\EwBase\EventListener\JsMerger: tags: ['event.listener'] - Evoweb\EwBase\EventListener\IsContentUsedOnPageLayout: - tags: ['event.listener'] - Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem: tags: ['backend.toolbar.item'] diff --git a/Configuration/Sets/EwBase/config.yaml b/Configuration/Sets/EwBase/config.yaml new file mode 100644 index 0000000..81addb3 --- /dev/null +++ b/Configuration/Sets/EwBase/config.yaml @@ -0,0 +1,2 @@ +name: evoweb/ew-base +label: evoWeb base package diff --git a/Configuration/Sets/EwBase/settings.definitions.yaml b/Configuration/Sets/EwBase/settings.definitions.yaml new file mode 100644 index 0000000..31732ee --- /dev/null +++ b/Configuration/Sets/EwBase/settings.definitions.yaml @@ -0,0 +1,33 @@ +settings: + ew-base.userCentrics.loader.preload: + default: '' + label: 'UserCentrics loader preloader' + type: string + description: 'Preloader for loader' + ew-base.userCentrics.loader.template: + default: '' + label: 'UserCentrics loader template' + type: string + description: 'Template for loader' + + ew-base.userCentrics.main.preload: + default: '' + label: 'UserCentrics main preloader' + type: string + description: 'Preloader for main' + ew-base.userCentrics.main.template: + default: '' + label: 'UserCentrics main template' + type: string + description: 'Template for main' + + ew-base.userCentrics.block.preload: + default: '' + label: 'UserCentrics block preloader' + type: string + description: 'Preloader for block' + ew-base.userCentrics.block.template: + default: '' + label: 'UserCentrics block template' + type: string + description: 'Template for block' diff --git a/Configuration/Sets/EwBase/settings.yaml b/Configuration/Sets/EwBase/settings.yaml new file mode 100644 index 0000000..a40b5a9 --- /dev/null +++ b/Configuration/Sets/EwBase/settings.yaml @@ -0,0 +1,22 @@ +ew-base: + userCentrics: + loader: + preload: | + + + + template: | + + main: + preload: | + + + + + template: | + + block: + preload: | + + template: | + diff --git a/ext_localconf.php b/ext_localconf.php index 5fd1db4..cca6d61 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -12,8 +12,6 @@ use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseParentPageRow; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline as BaseSiteTcaInline; use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage; -use TYPO3\CMS\Core\Information\Typo3Version; -use TYPO3\CMS\Core\Utility\GeneralUtility; call_user_func(function () { $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['ewb'] = [ 'Evoweb\\EwBase\\ViewHelpers' ]; @@ -46,24 +44,4 @@ call_user_func(function () { 'depends' => [ BaseSiteTcaInline::class ], 'before' => [ TcaSiteLanguage::class ], ]; - - if (empty($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics'] ?? [])) { - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['loader']['preload'] = - ' - -'; - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['loader']['template'] = - ''; - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['main']['preload'] = - ' - - -'; - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['main']['template'] = - ''; - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['block']['preload'] = - ''; - $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['block']['template'] = - ''; - } }); From 5a22d7c1523f2195f05069c80301421592e2ceef Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 17 May 2024 18:42:18 +0200 Subject: [PATCH 07/20] Refactory event listener registration --- Classes/EventListener/CssMerger.php | 2 ++ Classes/EventListener/JsMerger.php | 2 ++ Configuration/Services.yaml | 6 ------ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Classes/EventListener/CssMerger.php b/Classes/EventListener/CssMerger.php index dfd8de0..cd47be5 100755 --- a/Classes/EventListener/CssMerger.php +++ b/Classes/EventListener/CssMerger.php @@ -2,6 +2,7 @@ namespace Evoweb\EwBase\EventListener; +use TYPO3\CMS\Core\Attribute\AsEventListener; use TYPO3\CMS\Core\Configuration\ExtensionConfiguration; use TYPO3\CMS\Core\Page\AssetCollector; use TYPO3\CMS\Core\Page\Event\BeforeStylesheetsRenderingEvent; @@ -14,6 +15,7 @@ class CssMerger { } + #[AsEventListener('evoweb-ew-base-beforestylesheets', BeforeStylesheetsRenderingEvent::class)] public function __invoke(BeforeStylesheetsRenderingEvent $event): void { if ($event->isInline() && $event->isPriority()) { diff --git a/Classes/EventListener/JsMerger.php b/Classes/EventListener/JsMerger.php index 52d7381..7944435 100755 --- a/Classes/EventListener/JsMerger.php +++ b/Classes/EventListener/JsMerger.php @@ -2,6 +2,7 @@ namespace Evoweb\EwBase\EventListener; +use TYPO3\CMS\Core\Attribute\AsEventListener; use TYPO3\CMS\Core\Page\AssetCollector; use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -9,6 +10,7 @@ use TYPO3\CMS\Core\Utility\PathUtility; class JsMerger { + #[AsEventListener('evoweb-ew-base-beforejavascript', BeforeJavaScriptsRenderingEvent::class)] public function __invoke(BeforeJavaScriptsRenderingEvent $event): void { if ($event->isInline() && $event->isPriority()) { diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 7654eb3..b684568 100755 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -14,12 +14,6 @@ services: description: 'Command to list all content elements for a given subtree' schedulable: false - Evoweb\EwBase\EventListener\CssMerger: - tags: ['event.listener'] - - Evoweb\EwBase\EventListener\JsMerger: - tags: ['event.listener'] - Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem: tags: ['backend.toolbar.item'] From 0ce8dcdfbed1c2c30ca88328f070e91694c249e8 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Mon, 20 May 2024 20:02:24 +0200 Subject: [PATCH 08/20] Extend condition with check if request is set --- Classes/Configuration/AdditionalConfiguration.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/Configuration/AdditionalConfiguration.php b/Classes/Configuration/AdditionalConfiguration.php index 69530c8..675a92c 100755 --- a/Classes/Configuration/AdditionalConfiguration.php +++ b/Classes/Configuration/AdditionalConfiguration.php @@ -88,7 +88,7 @@ class AdditionalConfiguration 'constants', ' // condition should trigger different cache hashes - [request.getNormalizedParams().getHttpHost() == \'' . $remoteHost . '\'] + [request && request.getNormalizedParams().getHttpHost() == \'' . $remoteHost . '\'] config.baseURL = ' . $remoteHost . ' [end] ', From 932a5a028bf57c51988c83947e8f343a9a54656d Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Mon, 20 May 2024 20:02:50 +0200 Subject: [PATCH 09/20] Add query builder helper --- Classes/Services/QueryBuilderHelper.php | 311 ++++++++++++++++++++++++ 1 file changed, 311 insertions(+) create mode 100644 Classes/Services/QueryBuilderHelper.php diff --git a/Classes/Services/QueryBuilderHelper.php b/Classes/Services/QueryBuilderHelper.php new file mode 100644 index 0000000..efa5f70 --- /dev/null +++ b/Classes/Services/QueryBuilderHelper.php @@ -0,0 +1,311 @@ +expandListParameters( + $queryBuilder->getSQL(), + $queryBuilder->getParameters(), + $queryBuilder->getParameterTypes(), + ); + + array_walk($parameters, function ($value, $key) use (&$sql, $types) { + if ($types[$key] == ParameterType::STRING) { + $value = '\'' . $value . '\''; + } elseif ($types[$key] == ParameterType::INTEGER) { + $value = '' . $value; + } elseif ($types[$key] == ArrayParameterType::INTEGER) { + $value = implode(', ', $value); + } elseif ($types[$key] == ArrayParameterType::STRING) { + $value = $value ? '"' . implode('", "', $value) . '"' : '""'; + } + if (is_int($key)) { + $sql = substr_replace($sql, (string)$value, strpos($sql, '?'), 1); + } else { + $sql = str_replace(':' . $key, $value, $sql); + } + }); + + return $sql; + } + + /** + * Gets an array of the placeholders in a sql statements as keys and their positions in the query string. + * + * For a statement with positional parameters, returns a zero-indexed list of placeholder position. + * For a statement with named parameters, returns a map of placeholder positions to their parameter names. + */ + public function getPlaceholderPositions(string $statement, bool $isPositional = true): array + { + return $isPositional + ? $this->getPositionalPlaceholderPositions($statement) + : $this->getNamedPlaceholderPositions($statement); + } + + /** + * Returns a zero-indexed list of placeholder position. + * + * @return list + */ + private function getPositionalPlaceholderPositions(string $statement): array + { + return $this->collectPlaceholders( + $statement, + '?', + '\?', + function (string $_, int $placeholderPosition, int $fragmentPosition, array &$carry): void { + $carry[] = $placeholderPosition + $fragmentPosition; + } + ); + } + + /** + * Returns a map of placeholder positions to their parameter names. + * + * @return array + */ + private function getNamedPlaceholderPositions(string $statement): array + { + return $this->collectPlaceholders( + $statement, + ':', + '(?getUnquotedStatementFragments($statement) as $fragment) { + preg_match_all('/' . $token . '/', $fragment[0], $matches, PREG_OFFSET_CAPTURE); + foreach ($matches[0] as $placeholder) { + $collector($placeholder[0], $placeholder[1], $fragment[1], $carry); + } + } + + return $carry; + } + + /** + * For a positional query this method can rewrite the sql statement with regard to array parameters. + * + * @throws \Exception + */ + public function expandListParameters(string $query, array $params, array $types): array + { + $isPositional = is_int(key($params)); + $bindIndex = -1; + $arrayPositions = []; + + if ($isPositional) { + // make sure that $types has the same keys as $params + // to allow omitting parameters with unspecified types + $types += array_fill_keys(array_keys($params), null); + + ksort($params); + ksort($types); + } + + foreach ($types as $name => $type) { + ++$bindIndex; + + if ($type !== ArrayParameterType::INTEGER && $type !== ArrayParameterType::STRING) { + continue; + } + + if ($isPositional) { + $name = $bindIndex; + } + + $arrayPositions[$name] = false; + } + + // parameter are positional and no array parameter given + if ($isPositional && !$arrayPositions) { + return [$query, $params, $types]; + } + + return $isPositional + ? $this->preparePositionalParameters($query, $params, $types, $arrayPositions) + : $this->convertNamedParamsToPositionalParams($query, $params, $types, $arrayPositions); + } + + private function preparePositionalParameters( + string $query, + array $params, + array $types, + array $arrayPositions + ): array { + $paramOffset = 0; + $queryOffset = 0; + $params = array_values($params); + $types = array_values($types); + + $paramPos = $this->getPositionalPlaceholderPositions($query); + + foreach ($paramPos as $needle => $needlePos) { + if (!isset($arrayPositions[$needle])) { + continue; + } + + $needle += $paramOffset; + $needlePos += $queryOffset; + $count = count($params[$needle]); + + $params = array_merge( + array_slice($params, 0, $needle), + $params[$needle], + array_slice($params, $needle + 1) + ); + + $types = array_merge( + array_slice($types, 0, $needle), + $count ? + // array needles are at {@link \Doctrine\DBAL\ArrayParameterType} constants + array_fill(0, $count, $types[$needle]) : + [], + array_slice($types, $needle + 1) + ); + + $expandStr = $count ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1); + + $paramOffset += $count - 1; // Grows larger by number of parameters minus the replaced needle. + $queryOffset += strlen($expandStr) - 1; + } + + return [$query, $params, $types]; + } + + private function convertNamedParamsToPositionalParams( + string $query, + array $params, + array $types, + array $arrayPositions + ): array { + $queryOffset = 0; + $typesOrd = []; + $paramsOrd = []; + + $paramPos = $this->getNamedPlaceholderPositions($query); + + foreach ($paramPos as $pos => $paramName) { + $paramLen = strlen($paramName) + 1; + $value = $this->extractParam($paramName, $params, true); + + if (!isset($arrayPositions[$paramName]) && !isset($arrayPositions[':' . $paramName])) { + $pos += $queryOffset; + $queryOffset -= $paramLen - 1; + $paramsOrd[] = $value; + $typesOrd[] = $this->extractParam($paramName, $types, false, ParameterType::STRING); + $query = substr($query, 0, $pos) . '?' . substr($query, $pos + $paramLen); + + continue; + } + + $count = count($value); + $expandStr = $count > 0 ? implode(', ', array_fill(0, $count, '?')) : 'NULL'; + + foreach ($value as $val) { + $paramsOrd[] = $val; + $type = $this->extractParam($paramName, $types, false); + $typesOrd[] = match ($type) { + ArrayParameterType::INTEGER => ParameterType::INTEGER, + ArrayParameterType::STRING => ParameterType::STRING, + default => $type + }; + } + + $pos += $queryOffset; + $queryOffset += strlen($expandStr) - $paramLen; + $query = substr($query, 0, $pos) . $expandStr . substr($query, $pos + $paramLen); + } + + return [$query, $paramsOrd, $typesOrd]; + } + + /** + * Slice the SQL statement around pairs of quotes and + * return string fragments of outside SQL of quoted literals. + * Each fragment is captured as a 2-element array: + * + * 0 => matched fragment string, + * 1 => offset of fragment in $statement + */ + private function getUnquotedStatementFragments(string $statement): array + { + $literal = "(?:'(?:\\\\)+'|'(?:[^'\\\\]|\\\\'?|'')*')" + . '|' . '(?:"(?:\\\\)+"|"(?:[^"\\\\]|\\\\"?)*")' + . '|' . '(?:`(?:\\\\)+`|`(?:[^`\\\\]|\\\\`?)*`)' + . '|' . '(? Date: Wed, 22 May 2024 17:48:55 +0200 Subject: [PATCH 10/20] Add extension scanner ignore line --- Classes/EventListener/CssMerger.php | 1 + Classes/ViewHelpers/Be/ThumbnailViewHelper.php | 1 + 2 files changed, 2 insertions(+) diff --git a/Classes/EventListener/CssMerger.php b/Classes/EventListener/CssMerger.php index cd47be5..324c7ac 100755 --- a/Classes/EventListener/CssMerger.php +++ b/Classes/EventListener/CssMerger.php @@ -36,6 +36,7 @@ class CssMerger $assetCollector->addInlineStyleSheet('ew_base', $styles, [], ['priority' => true]); } else { $temporaryFile = GeneralUtility::writeStyleSheetContentToTemporaryFile($styles); + // @extensionScannerIgnoreLine $assetCollector->addStyleSheet('combined_styles', $temporaryFile); } } diff --git a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php index b2ffa84..f9bf1fd 100755 --- a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php +++ b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php @@ -75,6 +75,7 @@ class ThumbnailViewHelper extends AbstractViewHelper $row['uid'] => 'edit', ], ], + // @extensionScannerIgnoreLine 'returnUrl' => self::getRequest()->getAttribute('normalizedParams')->getRequestUri() . '#element-tt_content-' . $row['uid'], ]; From 503fec2f1b79644b6ea2239e138d52a7d42d4535 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sat, 25 May 2024 11:33:35 +0200 Subject: [PATCH 11/20] Add php-cs-fixer and run config --- .gitattributes | 5 + .gitignore | 11 + Build/Scripts/additionalTests.sh | 411 ++++++ Build/Scripts/runTests.sh | 1147 +++++++++++++++++ Build/Scripts/test.sh | 143 ++ Build/php-cs-fixer/config.php | 116 ++ Build/php-cs-fixer/header-comment.php | 75 ++ Classes/Command/ContentElementCommand.php | 4 +- .../Configuration/AdditionalConfiguration.php | 9 +- Classes/EventListener/CssMerger.php | 15 +- Classes/EventListener/JsMerger.php | 11 + Classes/Form/Element/PickColorFromImage.php | 4 +- .../UsercentricsDatabaseEditRow.php | 11 + .../UsercentricsTcaInline.php | 4 +- Classes/Hooks/UsercentricsPostRenderHook.php | 13 +- Classes/Services/QueryBuilderHelper.php | 7 +- Classes/ToolbarItems/ReleaseToolbarItem.php | 9 +- .../GridelementsToContainerMigration.php | 13 +- .../GridelementsToContainerService.php | 26 +- Classes/User/AssetPath.php | 11 + Classes/ViewHelpers/Array/AddViewHelper.php | 4 +- .../ViewHelpers/Array/ExplodeViewHelper.php | 16 +- .../Condition/InArrayViewHelper.php | 12 +- .../Condition/StringContainsViewHelper.php | 12 +- .../Context/DevelopmentViewHelper.php | 12 +- .../Context/ProductionViewHelper.php | 12 +- .../ViewHelpers/Context/StagingViewHelper.php | 14 +- Classes/ViewHelpers/HashViewHelper.php | 6 +- Classes/ViewHelpers/PublicPathViewHelper.php | 6 +- Classes/ViewHelpers/ReplaceViewHelper.php | 4 +- Classes/ViewHelpers/SvgViewHelper.php | 4 +- Classes/ViewHelpers/TrimViewHelper.php | 4 +- Classes/Xclass/SiteDatabaseEditRow.php | 19 +- Classes/Xclass/SiteTcaInline.php | 16 +- .../SiteConfiguration/site_usercentrics.php | 10 +- Configuration/TCA/Overrides/pages.php | 10 +- LICENSE.txt | 345 +++++ composer.json | 18 +- ext_localconf.php | 4 +- 39 files changed, 2476 insertions(+), 97 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100755 Build/Scripts/additionalTests.sh create mode 100755 Build/Scripts/runTests.sh create mode 100755 Build/Scripts/test.sh create mode 100644 Build/php-cs-fixer/config.php create mode 100644 Build/php-cs-fixer/header-comment.php create mode 100644 LICENSE.txt diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c1108e7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +/.github/ export-ignore +/Build/ export-ignore +/Tests/ export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4ebdfe --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.cache/ +.idea/ +Build/ +bin/ +Documentation-GENERATED-temp/ +typo3temp/ +var/ +public/ +vendor/ +composer.lock +.php-cs-fixer.cache diff --git a/Build/Scripts/additionalTests.sh b/Build/Scripts/additionalTests.sh new file mode 100755 index 0000000..49fb49a --- /dev/null +++ b/Build/Scripts/additionalTests.sh @@ -0,0 +1,411 @@ +#!/usr/bin/env bash + +# +# TYPO3 core test runner based on docker or podman +# + +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 10 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi +} + +cleanUp() { + ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}') + for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do + ${CONTAINER_BIN} kill ${ATTACHED_CONTAINER} >/dev/null + done + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null + else + ${CONTAINER_BIN} network rm -f ${NETWORK} >/dev/null + fi +} + +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. + case ${DBMS} in + mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" + if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; + esac +} + +cleanBuildFiles() { + echo -n "Clean builds ... " + rm -rf \ + Build/JavaScript \ + Build/node_modules \ + Documentation-GENERATED-temp + echo "done" +} + +cleanTestFiles() { + # test related + echo -n "Clean test related files ... " + rm -rf \ + bin/ \ + Build/phpunit \ + public/ \ + typo3temp/ \ + vendor/ \ + var/ \ + composer.lock + git checkout composer.json + echo "done" +} + +getPhpImageVersion() { + case ${1} in + 8.1) + echo -n "2.12" + ;; + 8.2) + echo -n "1.12" + ;; + 8.3) + echo -n "1.13" + ;; + esac +} + +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < + Specifies the test suite to run + - buildDocumentation: test build the documentation + - clean: clean up build, cache and testing related files and folders + - composerInstallPackage: install a package with composer + - lintXliff: test XLIFF language files + + -b + Container environment: + - podman (default) + - docker + + -p <8.1|8.2|8.3> + Specifies the PHP minor version to be used + - 8.1: use PHP 8.1 + - 8.2 (default): use PHP 8.2 + - 8.3: use PHP 8.3 + + -q + package to be installed by composer + + -r + parameters used with composer commands + + -h + Show this help. + + -v + Enable verbose script output. Shows variables and docker commands. + +Examples: + # Run install a package with composer + ./Build/Scripts/additionalTests.sh -p 8.2 -s composerInstallPackage "typo3/cms-core:13.0" + + # Test build the documentation + ./Build/Scripts/additionalTests.sh -s buildDocumentation + + # Test XLIFF language files + ./Build/Scripts/additionalTests.sh -s lintXliff +EOF +} + +# Test if docker exists, else exit out with error +if ! type "docker" >/dev/null; then + echo "This script relies on docker. Please install" >&2 + exit 1 +fi + +# Option defaults +TEST_SUITE="unit" +DBMS="sqlite" +DBMS_VERSION="" +PHP_VERSION="8.1" +PHP_XDEBUG_ON=0 +PHP_XDEBUG_PORT=9003 +ACCEPTANCE_HEADLESS=1 +EXTRA_TEST_OPTIONS="" +PHPUNIT_RANDOM="" +CGLCHECK_DRY_RUN="" +DATABASE_DRIVER="" +CHUNKS=0 +THISCHUNK=0 +CONTAINER_BIN="docker" + +SCRIPT_VERBOSE=0 +COMPOSER_PACKAGE="" +COMPOSER_PARAMETER="" + +# Option parsing updates above default vars +# Reset in case getopts has been used previously in the shell +OPTIND=1 +# Array for invalid options +INVALID_OPTIONS=() +# Simple option parsing based on getopts (! not getopt) +while getopts ":s:p:q:r:hv" OPT; do + case ${OPT} in + s) + TEST_SUITE=${OPTARG} + ;; + p) + PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + fi + ;; + q) + COMPOSER_PACKAGE=${OPTARG} + ;; + r) + COMPOSER_PARAMETER=${OPTARG} + ;; + h) + loadHelp + echo "${HELP}" + exit 0 + ;; + v) + SCRIPT_VERBOSE=1 + ;; + \?) + INVALID_OPTIONS+=("${OPTARG}") + ;; + :) + INVALID_OPTIONS+=("${OPTARG}") + ;; + esac +done + +# Exit on invalid options +if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then + echo "Invalid option(s):" >&2 + for I in "${INVALID_OPTIONS[@]}"; do + echo "-"${I} >&2 + done + echo >&2 + echo "Use \"./Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 +fi + +handleDbmsOptions + +COMPOSER_ROOT_VERSION="7.0.1" +HOST_UID=$(id -u) +HOST_PID=$(id -g) +USERSET="" +if [ $(uname) != "Darwin" ]; then + USERSET="--user $HOST_UID" +fi + +# Go to the directory this script is located, so everything else is relative +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd "$THIS_SCRIPT_DIR" || exit 1 +cd ../../ || exit 1 +CORE_ROOT="${PWD}" + +# Create .cache dir: composer and various npm jobs need this. +mkdir -p .cache +mkdir -p typo3temp/var/tests + +PHPSTAN_CONFIG_FILE="phpstan.local.neon" +IMAGE_PREFIX="docker.io/" +# Non-CI fetches TYPO3 images (php and nodejs) from ghcr.io +TYPO3_IMAGE_PREFIX="ghcr.io/" +CONTAINER_INTERACTIVE="-it --init" + +IS_CORE_CI=0 +# ENV var "CI" is set by gitlab-ci. We use it here to distinct 'local' and 'CI' environment. +if [ "${CI}" == "true" ]; then + IS_CORE_CI=1 + PHPSTAN_CONFIG_FILE="phpstan.ci.neon" + # In CI, we need to pull images from docker.io for the registry proxy to kick in. + TYPO3_IMAGE_PREFIX="docker.io/" + IMAGE_PREFIX="" + CONTAINER_INTERACTIVE="" +fi + + +IMAGE_APACHE="${TYPO3_IMAGE_PREFIX}typo3/core-testing-apache24:latest" +IMAGE_PHP="${TYPO3_IMAGE_PREFIX}typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):latest" +IMAGE_NODEJS="${TYPO3_IMAGE_PREFIX}typo3/core-testing-nodejs18:latest" +IMAGE_NODEJS_CHROME="${TYPO3_IMAGE_PREFIX}typo3/core-testing-nodejs18-chrome:latest" +IMAGE_ALPINE="${IMAGE_PREFIX}alpine:3.8" +IMAGE_SELENIUM="${IMAGE_PREFIX}selenium/standalone-chrome:4.11.0-20230801" +IMAGE_REDIS="${IMAGE_PREFIX}redis:4-alpine" +IMAGE_MEMCACHED="${IMAGE_PREFIX}memcached:1.5-alpine" +IMAGE_MARIADB="${IMAGE_PREFIX}mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="${IMAGE_PREFIX}mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="${IMAGE_PREFIX}postgres:${DBMS_VERSION}-alpine" +IMAGE_DOCUMENTATION="ghcr.io/t3docs/render-documentation:v3.0.dev30" +IMAGE_XLIFF="container.registry.gitlab.typo3.org/qa/example-extension:typo3-ci-xliff-lint" + +# Detect arm64 to use seleniarm image. +ARCH=$(uname -m) +if [ ${ARCH} = "arm64" ]; then + IMAGE_SELENIUM="${IMAGE_PREFIX}seleniarm/standalone-chromium:4.1.2-20220227" + echo "Architecture" ${ARCH} "requires" ${IMAGE_SELENIUM} "to run acceptance tests." +fi + +# Set $1 to first mass argument, this is the optional test file or test directory to execute +shift $((OPTIND - 1)) +TEST_FILE=${1} + +SUFFIX=$(echo $RANDOM) +NETWORK="typo3-core-${SUFFIX}" +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network $NETWORK --add-host "host.docker.internal:host-gateway" $USERSET -v ${CORE_ROOT}:${CORE_ROOT}" + +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " + PHP_FPM_OPTIONS="-d xdebug.mode=off" +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=host.docker.internal" + PHP_FPM_OPTIONS="-d xdebug.mode=debug -d xdebug.start_with_request=yes -d xdebug.client_host=host.docker.internal -d xdebug.client_port=${PHP_XDEBUG_PORT} -d memory_limit=256M" +fi +# if host uid is root, like for example on ci we need to set additional php-fpm command line options +if [ "${HOST_UID}" = 0 ]; then + PHP_FPM_OPTIONS+=" --allow-to-run-as-root" +fi + +# Suite execution +case ${TEST_SUITE} in + buildDocumentation) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} -v ${CORE_ROOT}:/project ghcr.io/typo3-documentation/render-guides:latest render Documentation + SUITE_EXIT_CODE=$? + ;; + clean) + cleanBuildFiles + cleanTestFiles + ;; + composerInstallPackage) + COMMAND="[ ${SCRIPT_VERBOSE} -eq 1 ] && set -x; composer require -W -n ${COMPOSER_PARAMETER} ${COMPOSER_PACKAGE};" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-require-package-${SUFFIX} -w ${CORE_ROOT} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/Build/.cache/composer ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + lintXliff) + COMMAND="[ ${SCRIPT_VERBOSE} -eq 1 ] && set -x; xmllint --schema /xliff-core-1.2-strict.xsd --noout *.xlf;" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-xliff-${SUFFIX} -w ${CORE_ROOT}/Resources/Private/Language ${IMAGE_XLIFF} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; +esac + +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +if [[ ${IS_CORE_CI} -eq 1 ]]; then + echo "Environment: CI" >&2 +else + echo "Environment: local" >&2 +fi +echo "PHP: ${PHP_VERSION}" >&2 +if [[ "${COMPOSER_PACKAGE}" != "" ]]; then + echo "Package: ${COMPOSER_PACKAGE}" >&2 +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. +exit $SUITE_EXIT_CODE diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh new file mode 100755 index 0000000..b929768 --- /dev/null +++ b/Build/Scripts/runTests.sh @@ -0,0 +1,1147 @@ +#!/usr/bin/env bash + +# +# TYPO3 core test runner based on docker or podman +# + +trap 'cleanUp;exit 2' SIGINT + +waitFor() { + local HOST=${1} + local PORT=${2} + local TESTCOMMAND=" + COUNT=0; + while ! nc -z ${HOST} ${PORT}; do + if [ \"\${COUNT}\" -gt 10 ]; then + echo \"Can not connect to ${HOST} port ${PORT}. Aborting.\"; + exit 1; + fi; + sleep 1; + COUNT=\$((COUNT + 1)); + done; + " + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name wait-for-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_ALPINE} /bin/sh -c "${TESTCOMMAND}" + if [[ $? -gt 0 ]]; then + kill -SIGINT -$$ + fi +} + +cleanUp() { + ATTACHED_CONTAINERS=$(${CONTAINER_BIN} ps --filter network=${NETWORK} --format='{{.Names}}') + for ATTACHED_CONTAINER in ${ATTACHED_CONTAINERS}; do + ${CONTAINER_BIN} kill ${ATTACHED_CONTAINER} >/dev/null + done + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} network rm ${NETWORK} >/dev/null + else + ${CONTAINER_BIN} network rm -f ${NETWORK} >/dev/null + fi +} + +handleDbmsOptions() { + # -a, -d, -i depend on each other. Validate input combinations and set defaults. + case ${DBMS} in + mariadb) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10.4" + if ! [[ ${DBMS_VERSION} =~ ^(10.4|10.5|10.6|10.7|10.8|10.9|10.10|10.11|11.0|11.1)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + mysql) + [ -z "${DATABASE_DRIVER}" ] && DATABASE_DRIVER="mysqli" + if [ "${DATABASE_DRIVER}" != "mysqli" ] && [ "${DATABASE_DRIVER}" != "pdo_mysql" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="8.0" + if ! [[ ${DBMS_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + postgres) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + [ -z "${DBMS_VERSION}" ] && DBMS_VERSION="10" + if ! [[ ${DBMS_VERSION} =~ ^(10|11|12|13|14|15|16)$ ]]; then + echo "Invalid combination -d ${DBMS} -i ${DBMS_VERSION}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + sqlite) + if [ -n "${DATABASE_DRIVER}" ]; then + echo "Invalid combination -d ${DBMS} -a ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + if [ -n "${DBMS_VERSION}" ]; then + echo "Invalid combination -d ${DBMS} -i ${DATABASE_DRIVER}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + fi + ;; + *) + echo "Invalid option -d ${DBMS}" >&2 + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 + ;; + esac +} + +cleanBuildFiles() { + echo -n "Clean builds ... " + rm -rf \ + Build/JavaScript \ + Build/node_modules + echo "done" +} + +cleanCacheFiles() { + echo -n "Clean caches ... " + rm -rf \ + .cache \ + Build/.cache \ + Build/composer/.cache/ \ + .php-cs-fixer.cache + echo "done" +} + +cleanTestFiles() { + # composer distribution test + echo -n "Clean composer distribution test ... " + rm -rf \ + Build/composer/composer.json \ + Build/composer/composer.lock \ + Build/composer/public/index.php \ + Build/composer/public/typo3 \ + Build/composer/public/typo3conf/ext \ + Build/composer/var/ \ + Build/composer/vendor/ + echo "done" + + # test related + echo -n "Clean test related files ... " + rm -rf \ + Build/phpunit/FunctionalTests-Job-*.xml \ + typo3/sysext/core/Tests/AcceptanceTests-Job-* \ + typo3/sysext/core/Tests/Acceptance/Support/_generated \ + typo3temp/var/tests/ + echo "done" +} + +cleanRenderedDocumentationFiles() { + echo -n "Clean rendered documentation files ... " + rm -rf \ + ../../../typo3/sysext/*/Documentation-GENERATED-temp + echo "done" +} + +getPhpImageVersion() { + case ${1} in + 8.1) + echo -n "2.12" + ;; + 8.2) + echo -n "1.12" + ;; + 8.3) + echo -n "1.13" + ;; + esac +} + +loadHelp() { + # Load help text into $HELP + read -r -d '' HELP < + Specifies the test suite to run + - acceptance: main application acceptance tests + - acceptanceComposer: main application acceptance tests + - acceptanceInstall: installation acceptance tests, only with -d mariadb|postgres|sqlite + - buildCss: execute scss to css builder + - buildJavascript: execute typescript to javascript builder + - cgl: test and fix all core php files + - cglGit: test and fix latest committed patch for CGL compliance + - cglHeader: test and fix file header for all core php files + - cglHeaderGit: test and fix latest committed patch for CGL file header compliance + - checkAnnotations: check php code for allowed annotations + - checkBom: check UTF-8 files do not contain BOM + - checkComposer: check composer.json files for version integrity + - checkExceptionCodes: test core for duplicate exception codes + - checkExtensionScannerRst: test all .rst files referenced by extension scanner exist + - checkFilePathLength: test core file paths do not exceed maximum length + - checkGitSubmodule: test core git has no sub modules defined + - checkGruntClean: Verify "grunt build" is clean. Warning: Executes git commands! Usually used in CI only. + - checkIsoDatabase: Verify "updateIsoDatabase.php" does not change anything. + - checkNamespaceIntegrity: Verify namespace integrity in class and test code files are in good shape. + - checkPermissions: test some core files for correct executable bits + - checkRst: test .rst files for integrity + - checkTestClassFinal: check test case classes are final + - checkTestMethodsPrefix: check tests methods do not start with "test" + - clean: clean up build, cache and testing related files and folders + - cleanBuild: clean up build related files and folders + - cleanCache: clean up cache related files and folders + - cleanRenderedDocumentation: clean up rendered documentation files and folders (Documentation-GENERATED-temp) + - cleanTests: clean up test related files and folders + - composer: "composer" command dispatcher, to execute various composer commands + - composerInstall: "composer install" + - composerInstallMax: "composer update", with no platform.php config. + - composerInstallMin: "composer update --prefer-lowest", with platform.php set to PHP version x.x.0. + - composerTestDistribution: "composer update" in Build/composer to verify core dependencies + - composerValidate: "composer validate" + - functional: PHP functional tests + - functionalDeprecated: deprecated PHP functional tests + - lintPhp: PHP linting + - lintScss: SCSS linting + - lintTypescript: TS linting + - lintHtml: HTML linting + - listExceptionCodes: list core exception codes in JSON format + - npm: "npm" command dispatcher, to execute various npm commands directly + - phpstan: phpstan tests + - phpstanGenerateBaseline: regenerate phpstan baseline, handy after phpstan updates + - unit (default): PHP unit tests + - unitDeprecated: deprecated PHP unit tests + - unitJavascript: JavaScript unit tests + - unitRandom: PHP unit tests in random order, add -o to use specific seed + + -b + Container environment: + - podman (default) + - docker + + -a + Only with -s functional|functionalDeprecated + Specifies to use another driver, following combinations are available: + - mysql + - mysqli (default) + - pdo_mysql + - mariadb + - mysqli (default) + - pdo_mysql + + -d + Only with -s functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall + Specifies on which DBMS tests are performed + - sqlite: (default): use sqlite + - mariadb: use mariadb + - mysql: use MySQL + - postgres: use postgres + + -i version + Specify a specific database version + With "-d mariadb": + - 10.4 short-term, maintained until 2024-06-18 (default) + - 10.5 short-term, maintained until 2025-06-24 + - 10.6 long-term, maintained until 2026-06 + - 10.7 short-term, no longer maintained + - 10.8 short-term, maintained until 2023-05 + - 10.9 short-term, maintained until 2023-08 + - 10.10 short-term, maintained until 2023-11 + - 10.11 long-term, maintained until 2028-02 + - 11.0 development series + - 11.1 short-term development series + With "-d mysql": + - 8.0 maintained until 2026-04 (default) LTS + - 8.1 unmaintained since 2023-10 + - 8.2 unmaintained since 2024-01 + - 8.3 maintained until 2024-04 + With "-d postgres": + - 10 unmaintained since 2022-11-10 (default) + - 11 unmaintained since 2023-11-09 + - 12 maintained until 2024-11-14 + - 13 maintained until 2025-11-13 + - 14 maintained until 2026-11-12 + - 15 maintained until 2027-11-11 + - 16 maintained until 2028-11-09 + + -c + Only with -s functional|acceptance + Hack functional or acceptance tests into #numberOfChunks pieces and run tests of #chunk. + Example -c 3/13 + + -p <8.1|8.2|8.3> + Specifies the PHP minor version to be used + - 8.1: use PHP 8.1 + - 8.2 (default): use PHP 8.2 + - 8.3: use PHP 8.3 + + -t sets|systemplate + Only with -s acceptance|acceptanceComposer + Specifies which frontend rendering mechanism should be used + - sets: (default): use site sets + - systemplate: use sys_template records + + -e "" (DEPRECATED). + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom|acceptance + Additional options to send to phpunit (unit & functional tests) or codeception (acceptance + tests). For phpunit, options starting with "--" must be added after options starting with "-". + Example -e "-d memory_limit=-1 --filter filterByValueRecursiveCorrectlyFiltersArray" to enable verbose output AND filter tests + named "canRetrieveValueWithGP" + + DEPRECATED - pass arguments after the `--` separator directly. For example, instead of + Build/Scripts/runTests.sh -s unit -e "--filter filterByValueRecursiveCorrectlyFiltersArray" + use + Build/Scripts/runTests.sh -s unit -- --filter filterByValueRecursiveCorrectlyFiltersArray + + -g + Only with -s acceptance|acceptanceComposer|acceptanceInstall + Activate selenium grid as local port to watch browser clicking around. Can be surfed using + http://localhost:7900/. A browser tab is opened automatically if xdg-open is installed. + + -x + Only with -s functional|functionalDeprecated|unit|unitDeprecated|unitRandom|acceptance|acceptanceComposer|acceptanceInstall + Send information to host instance for test or system under test break points. This is especially + useful if a local PhpStorm instance is listening on default xdebug port 9003. A different port + can be selected with -y + + -y + Send xdebug information to a different port than default 9003 if an IDE like PhpStorm + is not listening on default port. + + -o + Only with -s unitRandom + Set specific random seed to replay a random run in this order again. The phpunit randomizer + outputs the used seed at the end (in gitlab core testing logs, too). Use that number to + replay the unit tests in that order. + + -n + Only with -s cgl|cglGit|cglHeader|cglHeaderGit + Activate dry-run in CGL check that does not actively change files and only prints broken ones. + + -u + Update existing typo3/core-testing-* container images and remove obsolete dangling image versions. + Use this if weird test errors occur. + + -h + Show this help. + +Examples: + # Run all core unit tests using PHP 8.2 + ./Build/Scripts/runTests.sh + ./Build/Scripts/runTests.sh -s unit + + # Run all core units tests and enable xdebug (have a PhpStorm listening on port 9003!) + ./Build/Scripts/runTests.sh -x + + # Run unit tests in phpunit with xdebug on PHP 8.3 and filter for test filterByValueRecursiveCorrectlyFiltersArray + ./Build/Scripts/runTests.sh -x -p 8.3 -- --filter filterByValueRecursiveCorrectlyFiltersArray + + # Run functional tests in phpunit with a filtered test method name in a specified file + # example will currently execute two tests, both of which start with the search term + ./Build/Scripts/runTests.sh -s functional -- \ + --filter datetimeInstanceCanBePersistedToDatabaseIfTypeIsExplicitlySpecified \ + typo3/sysext/core/Tests/Functional/Database/ConnectionTest.php + + # Run functional tests on postgres with xdebug, php 8.3 and execute a restricted set of tests + ./Build/Scripts/runTests.sh -x -p 8.3 -s functional -d postgres typo3/sysext/core/Tests/Functional/Authentication + + # Run functional tests on postgres 11 + ./Build/Scripts/runTests.sh -s functional -d postgres -i 11 + + # Run restricted set of application acceptance tests + ./Build/Scripts/runTests.sh -s acceptance typo3/sysext/core/Tests/Acceptance/Application/Login/BackendLoginCest.php:loginButtonMouseOver + + # Run installer tests of a new instance on sqlite + ./Build/Scripts/runTests.sh -s acceptanceInstall -d sqlite + + # Run composer require to require a dependency + ./Build/Scripts/runTests.sh -s composer -- require --dev typo3/testing-framework:dev-main + + # Some composer command examples + ./Build/Scripts/runTests.sh -s composer -- dumpautoload + ./Build/Scripts/runTests.sh -s composer -- info | grep "symfony" + + # Some npm command examples + ./Build/Scripts/runTests.sh -s npm -- audit + ./Build/Scripts/runTests.sh -s npm -- ci + ./Build/Scripts/runTests.sh -s npm -- run build + ./Build/Scripts/runTests.sh -s npm -- run watch:build + ./Build/Scripts/runTests.sh -s npm -- install --save bootstrap@^5.3.2 +EOF +} + +# Test if docker exists, else exit out with error +if ! type "docker" >/dev/null 2>&1 && ! type "podman" >/dev/null 2>&1; then + echo "This script relies on docker or podman. Please install" >&2 + exit 1 +fi + +# Go to the directory this script is located, so everything else is relative +# to this dir, no matter from where this script is called, then go up two dirs. +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd "$THIS_SCRIPT_DIR" || exit 1 +cd ../../ || exit 1 +CORE_ROOT="${PWD}" + +# Default variables +TEST_SUITE="unit" +DBMS="sqlite" +DBMS_VERSION="" +PHP_VERSION="8.2" +PHP_XDEBUG_ON=0 +PHP_XDEBUG_PORT=9003 +ACCEPTANCE_HEADLESS=1 +ACCEPTANCE_TOPIC="sets" +EXTRA_TEST_OPTIONS="" +PHPUNIT_RANDOM="" +CGLCHECK_DRY_RUN="" +DATABASE_DRIVER="" +CHUNKS=0 +THISCHUNK=0 +CONTAINER_BIN="" +COMPOSER_ROOT_VERSION="13.2.x-dev" +PHPSTAN_CONFIG_FILE="phpstan.local.neon" +CONTAINER_INTERACTIVE="-it --init" +HOST_UID=$(id -u) +HOST_PID=$(id -g) +USERSET="" +SUFFIX=$(echo $RANDOM) +NETWORK="typo3-core-${SUFFIX}" +CI_PARAMS="${CI_PARAMS:-}" +CONTAINER_HOST="host.docker.internal" + +# Option parsing updates above default vars +# Reset in case getopts has been used previously in the shell +OPTIND=1 +# Array for invalid options +INVALID_OPTIONS=() +# Simple option parsing based on getopts (! not getopt) +while getopts ":a:b:s:c:d:i:t:p:e:xy:o:nhug" OPT; do + case ${OPT} in + s) + TEST_SUITE=${OPTARG} + ;; + b) + if ! [[ ${OPTARG} =~ ^(docker|podman)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + fi + CONTAINER_BIN=${OPTARG} + ;; + a) + DATABASE_DRIVER=${OPTARG} + ;; + c) + if ! [[ ${OPTARG} =~ ^([0-9]+\/[0-9]+)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + else + # Split "2/13" - run chunk 2 of 13 chunks + THISCHUNK=$(echo "${OPTARG}" | cut -d '/' -f1) + CHUNKS=$(echo "${OPTARG}" | cut -d '/' -f2) + fi + ;; + d) + DBMS=${OPTARG} + ;; + i) + DBMS_VERSION=${OPTARG} + ;; + p) + PHP_VERSION=${OPTARG} + if ! [[ ${PHP_VERSION} =~ ^(8.1|8.2|8.3)$ ]]; then + INVALID_OPTIONS+=("${OPTARG}") + fi + ;; + e) + EXTRA_TEST_OPTIONS=${OPTARG} + ;; + g) + ACCEPTANCE_HEADLESS=0 + ;; + t) + ACCEPTANCE_TOPIC=${OPTARG} + ;; + x) + PHP_XDEBUG_ON=1 + ;; + y) + PHP_XDEBUG_PORT=${OPTARG} + ;; + o) + PHPUNIT_RANDOM="--random-order-seed=${OPTARG}" + ;; + n) + CGLCHECK_DRY_RUN="-n" + ;; + h) + loadHelp + echo "${HELP}" + exit 0 + ;; + u) + TEST_SUITE=update + ;; + \?) + INVALID_OPTIONS+=("${OPTARG}") + ;; + :) + INVALID_OPTIONS+=("${OPTARG}") + ;; + esac +done + +# Exit on invalid options +if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then + echo "Invalid option(s):" >&2 + for I in "${INVALID_OPTIONS[@]}"; do + echo "-"${I} >&2 + done + echo >&2 + echo "Use \".Build/Scripts/runTests.sh -h\" to display help and valid options" >&2 + exit 1 +fi + +handleDbmsOptions + +# ENV var "CI" is set by gitlab-ci. Use it to force some CI details. +if [ "${CI}" == "true" ]; then + PHPSTAN_CONFIG_FILE="phpstan.ci.neon" + CONTAINER_INTERACTIVE="" +fi + +# determine default container binary to use: 1. podman 2. docker +if [[ -z "${CONTAINER_BIN}" ]]; then + if type "podman" >/dev/null 2>&1; then + CONTAINER_BIN="podman" + elif type "docker" >/dev/null 2>&1; then + CONTAINER_BIN="docker" + fi +fi + +if [ $(uname) != "Darwin" ] && [ ${CONTAINER_BIN} = "docker" ]; then + # Run docker jobs as current user to prevent permission issues. Not needed with podman. + USERSET="--user $HOST_UID" +fi + +if ! type ${CONTAINER_BIN} >/dev/null 2>&1; then + echo "Selected container environment \"${CONTAINER_BIN}\" not found. Please install or use -b option to select one." >&2 + exit 1 +fi + +IMAGE_APACHE="ghcr.io/typo3/core-testing-apache24:1.3" +IMAGE_PHP="ghcr.io/typo3/core-testing-$(echo "php${PHP_VERSION}" | sed -e 's/\.//'):$(getPhpImageVersion $PHP_VERSION)" + +IMAGE_NODEJS="ghcr.io/typo3/core-testing-nodejs22:1.0" +IMAGE_NODEJS_CHROME="ghcr.io/typo3/core-testing-nodejs22-chrome:1.0" +IMAGE_ALPINE="docker.io/alpine:3.8" +IMAGE_SELENIUM="docker.io/selenium/standalone-chrome:4.11.0-20230801" +IMAGE_REDIS="docker.io/redis:4-alpine" +IMAGE_MEMCACHED="docker.io/memcached:1.5-alpine" +IMAGE_MARIADB="docker.io/mariadb:${DBMS_VERSION}" +IMAGE_MYSQL="docker.io/mysql:${DBMS_VERSION}" +IMAGE_POSTGRES="docker.io/postgres:${DBMS_VERSION}-alpine" + +# Detect arm64 to use seleniarm image. +ARCH=$(uname -m) +if [ ${ARCH} = "arm64" ]; then + IMAGE_SELENIUM="docker.io/seleniarm/standalone-chromium:4.10.0-20230615" +fi + +# Remove handled options and leaving the rest in the line, so it can be passed raw to commands +shift $((OPTIND - 1)) + +# Create .cache dir: composer and various npm jobs need this. +mkdir -p .cache +mkdir -p typo3temp/var/tests + +${CONTAINER_BIN} network create ${NETWORK} >/dev/null + +if [ ${CONTAINER_BIN} = "docker" ]; then + # docker needs the add-host for xdebug remote debugging. podman has host.container.internal built in + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} --rm --network ${NETWORK} --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}" +else + # podman + CONTAINER_HOST="host.containers.internal" + CONTAINER_COMMON_PARAMS="${CONTAINER_INTERACTIVE} ${CI_PARAMS} --rm --network ${NETWORK} -v ${CORE_ROOT}:${CORE_ROOT} -w ${CORE_ROOT}" +fi + +if [ ${PHP_XDEBUG_ON} -eq 0 ]; then + XDEBUG_MODE="-e XDEBUG_MODE=off" + XDEBUG_CONFIG=" " + PHP_FPM_OPTIONS="-d xdebug.mode=off" +else + XDEBUG_MODE="-e XDEBUG_MODE=debug -e XDEBUG_TRIGGER=foo" + XDEBUG_CONFIG="client_port=${PHP_XDEBUG_PORT} client_host=${CONTAINER_HOST}" + PHP_FPM_OPTIONS="-d xdebug.mode=debug -d xdebug.start_with_request=yes -d xdebug.client_host=${CONTAINER_HOST} -d xdebug.client_port=${PHP_XDEBUG_PORT} -d memory_limit=256M" +fi + +# Suite execution +case ${TEST_SUITE} in + acceptance) + CODECEPION_ENV="--env ci,classic,${ACCEPTANCE_TOPIC}" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,classic,headless,${ACCEPTANCE_TOPIC}" + fi + if [ "${CHUNKS}" -gt 0 ]; then + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-splitter-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/splitAcceptanceTests.php -v ${CHUNKS} + COMMAND=(bin/codecept run Application -d -g AcceptanceTests-Job-${THISCHUNK} -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + else + COMMAND=(bin/codecept run Application -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + fi + SELENIUM_GRID="" + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + SELENIUM_GRID="-p 7900:7900 -e SE_VNC_NO_PASSWORD=1 -e VNC_NO_PASSWORD=1" + fi + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance" "${CORE_ROOT}/typo3temp/var/tests/AcceptanceReports" + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/acceptance" + APACHE_OPTIONS="-e APACHE_RUN_USER=#${HOST_UID} -e APACHE_RUN_SERVERNAME=web -e APACHE_RUN_GROUP=#${HOST_PID} -e APACHE_RUN_DOCROOT=${CORE_ROOT}/typo3temp/var/tests/acceptance -e PHPFPM_HOST=phpfpm -e PHPFPM_PORT=9000" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d ${SELENIUM_GRID} --name ac-chrome-${SUFFIX} --network ${NETWORK} --network-alias chrome --tmpfs /dev/shm:rw,nosuid,nodev,noexec ${IMAGE_SELENIUM} >/dev/null + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} run --rm -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -e PHPFPM_USER=${HOST_UID} -e PHPFPM_GROUP=${HOST_PID} -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web --add-host "${CONTAINER_HOST}:host-gateway" -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + else + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm ${USERSET} -e PHPFPM_USER=0 -e PHPFPM_GROUP=0 -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm -R ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + fi + waitFor chrome 4444 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + waitFor chrome 7900 + fi + waitFor web 80 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "xdg-open" >/dev/null; then + xdg-open http://localhost:7900/?autoconnect=1 >/dev/null + elif [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "open" >/dev/null; then + open http://localhost:7900/?autoconnect=1 >/dev/null + fi + case ${DBMS} in + mariadb) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-ac-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mariadb-ac-${SUFFIX}" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-mariadb ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + mysql) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-ac-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mysql-ac-${SUFFIX}" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-mysql ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-ac-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-ac-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=func_test -e typo3DatabaseUsername=funcu -e typo3DatabasePassword=funcp -e typo3DatabaseHost=postgres-ac-${SUFFIX}" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-postgres ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + sqlite) + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-sqlite ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + esac + ;; + acceptanceComposer) + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance-composer" "${CORE_ROOT}/typo3temp/var/tests/AcceptanceReports" + + PREPAREPARAMS="" + TESTPARAMS="" + case ${DBMS} in + mariadb) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=acp -e MYSQL_DATABASE=ac_test --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-ac-${SUFFIX} 3306 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=mysqli -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=root -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=mariadb-ac-${SUFFIX} -e TYPO3_DB_PORT=3306" + TESTPARAMS="-e typo3DatabaseName=ac_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mariadb-ac-${SUFFIX}" + ;; + mysql) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-ac-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=acp -e MYSQL_DATABASE=ac_test --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-ac-${SUFFIX} 3306 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=mysqli -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=root -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=mysql-ac-${SUFFIX} -e TYPO3_DB_PORT=3306" + TESTPARAMS="-e typo3DatabaseName=ac_test -e typo3DatabaseUsername=root -e typo3DatabasePassword=funcp -e typo3DatabaseHost=mysql-ac-${SUFFIX}" + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-ac-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_DB=ac_test -e POSTGRES_PASSWORD=acp -e POSTGRES_USER=ac_test --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-ac-${SUFFIX} 5432 + PREPAREPARAMS="-e TYPO3_DB_DRIVER=postgres -e TYPO3_DB_DBNAME=ac_test -e TYPO3_DB_USERNAME=ac_test -e TYPO3_DB_PASSWORD=acp -e TYPO3_DB_HOST=postgres-ac-${SUFFIX} -e TYPO3_DB_PORT=5432" + TESTPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=ac_test -e typo3DatabaseUsername=ac_test -e typo3DatabasePassword=acp -e typo3DatabaseHost=postgres-ac-${SUFFIX}" + ;; + sqlite) + PREPAREPARAMS="-e TYPO3_DB_DRIVER=sqlite" + TESTPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + ;; + esac + + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name acceptance-prepare ${XDEBUG_MODE} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${PREPAREPARAMS} ${IMAGE_PHP} "${CORE_ROOT}/Build/Scripts/setupAcceptanceComposer.sh" "typo3temp/var/tests/acceptance-composer" sqlite "" "${ACCEPTANCE_TOPIC}" + SUITE_EXIT_CODE=$? + if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + CODECEPION_ENV="--env ci,composer,${ACCEPTANCE_TOPIC}" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,composer,headless,${ACCEPTANCE_TOPIC}" + fi + if [ "${CHUNKS}" -gt 0 ]; then + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-splitter-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/splitAcceptanceTests.php -v ${CHUNKS} + COMMAND=(bin/codecept run Application -d -g AcceptanceTests-Job-${THISCHUNK} -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + else + COMMAND=(bin/codecept run Application -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} "$@" --html reports.html) + fi + SELENIUM_GRID="" + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + SELENIUM_GRID="-p 7900:7900 -e SE_VNC_NO_PASSWORD=1 -e VNC_NO_PASSWORD=1" + fi + APACHE_OPTIONS="-e APACHE_RUN_USER=#${HOST_UID} -e APACHE_RUN_SERVERNAME=web -e APACHE_RUN_GROUP=#${HOST_PID} -e APACHE_RUN_DOCROOT=${CORE_ROOT}/typo3temp/var/tests/acceptance-composer/public -e PHPFPM_HOST=phpfpm -e PHPFPM_PORT=9000" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d ${SELENIUM_GRID} --name ac-chrome-${SUFFIX} --network ${NETWORK} --network-alias chrome --tmpfs /dev/shm:rw,nosuid,nodev,noexec ${IMAGE_SELENIUM} >/dev/null + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} run --rm -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -e PHPFPM_USER=${HOST_UID} -e PHPFPM_GROUP=${HOST_PID} -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web --add-host "${CONTAINER_HOST}:host-gateway" -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + else + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm ${USERSET} -e PHPFPM_USER=0 -e PHPFPM_GROUP=0 -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm -R ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-web-${SUFFIX} --network ${NETWORK} --network-alias web -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + fi + waitFor chrome 4444 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + waitFor chrome 7900 + fi + waitFor web 80 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "xdg-open" >/dev/null; then + xdg-open http://localhost:7900/?autoconnect=1 >/dev/null + elif [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "open" >/dev/null; then + open http://localhost:7900/?autoconnect=1 >/dev/null + fi + + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-${DBMS}-composer ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${TESTPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + fi + ;; + acceptanceInstall) + SELENIUM_GRID="" + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + SELENIUM_GRID="-p 7900:7900 -e SE_VNC_NO_PASSWORD=1 -e VNC_NO_PASSWORD=1" + fi + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance" "${CORE_ROOT}/typo3temp/var/tests/AcceptanceReports" + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/acceptance" + APACHE_OPTIONS="-e APACHE_RUN_USER=#${HOST_UID} -e APACHE_RUN_SERVERNAME=web -e APACHE_RUN_GROUP=#${HOST_PID} -e APACHE_RUN_DOCROOT=${CORE_ROOT}/typo3temp/var/tests/acceptance -e PHPFPM_HOST=phpfpm -e PHPFPM_PORT=9000" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d ${SELENIUM_GRID} --name ac-install-chrome-${SUFFIX} --network ${NETWORK} --network-alias chrome --tmpfs /dev/shm:rw,nosuid,nodev,noexec ${IMAGE_SELENIUM} >/dev/null + if [ ${CONTAINER_BIN} = "docker" ]; then + ${CONTAINER_BIN} run --rm -d --name ac-install-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm --add-host "${CONTAINER_HOST}:host-gateway" ${USERSET} -e PHPFPM_USER=${HOST_UID} -e PHPFPM_GROUP=${HOST_PID} -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm -d --name ac-install-web-${SUFFIX} --network ${NETWORK} --network-alias web --add-host "${CONTAINER_HOST}:host-gateway" -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + else + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-install-phpfpm-${SUFFIX} --network ${NETWORK} --network-alias phpfpm ${USERSET} -e PHPFPM_USER=0 -e PHPFPM_GROUP=0 -v ${CORE_ROOT}:${CORE_ROOT} ${IMAGE_PHP} php-fpm -R ${PHP_FPM_OPTIONS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} -d --name ac-install-web-${SUFFIX} --network ${NETWORK} --network-alias web -v ${CORE_ROOT}:${CORE_ROOT} ${APACHE_OPTIONS} ${IMAGE_APACHE} >/dev/null + fi + waitFor chrome 4444 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ]; then + waitFor chrome 7900 + fi + waitFor web 80 + if [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "xdg-open" >/dev/null; then + xdg-open http://localhost:7900/?autoconnect=1 >/dev/null + elif [ "${ACCEPTANCE_HEADLESS}" -eq 0 ] && type "open" >/dev/null; then + open http://localhost:7900/?autoconnect=1 >/dev/null + fi + case ${DBMS} in + mariadb) + CODECEPION_ENV="--env ci,mysql" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,mysql,headless" + fi + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-ac-install-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-ac-install-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3InstallMysqlDatabaseName=func_test -e typo3InstallMysqlDatabaseUsername=root -e typo3InstallMysqlDatabasePassword=funcp -e typo3InstallMysqlDatabaseHost=mariadb-ac-install-${SUFFIX}" + COMMAND="bin/codecept run Install -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} --html reports.html" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-install-mariadb ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + mysql) + CODECEPION_ENV="--env ci,mysql" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,mysql,headless" + fi + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-ac-install-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-ac-install-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3InstallMysqlDatabaseName=func_test -e typo3InstallMysqlDatabaseUsername=root -e typo3InstallMysqlDatabasePassword=funcp -e typo3InstallMysqlDatabaseHost=mysql-ac-install-${SUFFIX}" + COMMAND="bin/codecept run Install -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} --html reports.html" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-install-mysql ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + postgres) + CODECEPION_ENV="--env ci,postgresql" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,postgresql,headless" + fi + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-ac-install-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-ac-install-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3InstallPostgresqlDatabasePort=5432 -e typo3InstallPostgresqlDatabaseName=${USER} -e typo3InstallPostgresqlDatabaseHost=postgres-ac-install-${SUFFIX} -e typo3InstallPostgresqlDatabaseUsername=funcu -e typo3InstallPostgresqlDatabasePassword=funcp" + COMMAND="bin/codecept run Install -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} --html reports.html" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-install-postgres ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + sqlite) + rm -rf "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/acceptance-sqlite-dbs/" + CODECEPION_ENV="--env ci,sqlite" + if [ "${ACCEPTANCE_HEADLESS}" -eq 1 ]; then + CODECEPION_ENV="--env ci,sqlite,headless" + fi + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite" + COMMAND="bin/codecept run Install -d -c typo3/sysext/core/Tests/codeception.yml ${EXTRA_TEST_OPTIONS} ${CODECEPION_ENV} --html reports.html" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name ac-install-sqlite ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + esac + ;; + buildCss) + COMMAND="cd Build; npm ci || exit 1; node_modules/grunt/bin/grunt css" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name build-css-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + buildJavascript) + COMMAND="cd Build/; npm ci || exit 1; node_modules/grunt/bin/grunt scripts" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name build-js-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + cgl) + # Active dry-run for cgl needs not "-n" but specific options + if [ -n "${CGLCHECK_DRY_RUN}" ]; then + CGLCHECK_DRY_RUN="--dry-run --diff" + fi + COMMAND="php -dxdebug.mode=off bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --path-mode intersection --config=Build/php-cs-fixer/config.php ." + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-${SUFFIX} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + cglGit) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-git-${SUFFIX} ${IMAGE_PHP} Build/Scripts/cglFixMyCommit.sh ${CGLCHECK_DRY_RUN} + SUITE_EXIT_CODE=$? + ;; + cglHeader) + # Active dry-run for cgl needs not "-n" but specific options + if [ -n "${CGLCHECK_DRY_RUN}" ]; then + CGLCHECK_DRY_RUN="--dry-run --diff" + fi + COMMAND="php -dxdebug.mode=off bin/php-cs-fixer fix -v ${CGLCHECK_DRY_RUN} --path-mode intersection --config=Build/php-cs-fixer/header-comment.php ." + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-header-${SUFFIX} ${IMAGE_PHP} ${COMMAND} + SUITE_EXIT_CODE=$? + ;; + cglHeaderGit) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name cgl-header-git-${SUFFIX} ${IMAGE_PHP} Build/Scripts/cglFixMyCommitFileHeader.sh ${CGLCHECK_DRY_RUN} + SUITE_EXIT_CODE=$? + ;; + checkAnnotations) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-annotations-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/annotationChecker.php + SUITE_EXIT_CODE=$? + ;; + checkTestClassFinal) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-test-classes-final-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/testClassFinalChecker.php + SUITE_EXIT_CODE=$? + ;; + checkTestMethodsPrefix) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-test-methods-prefix-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/testMethodPrefixChecker.php + SUITE_EXIT_CODE=$? + ;; + checkBom) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-utf8bom-${SUFFIX} ${IMAGE_PHP} Build/Scripts/checkUtf8Bom.sh + SUITE_EXIT_CODE=$? + ;; + checkComposer) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-composer-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/checkIntegrityComposer.php + SUITE_EXIT_CODE=$? + ;; + checkExceptionCodes) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-exception-codes-${SUFFIX} ${IMAGE_PHP} Build/Scripts/duplicateExceptionCodeCheck.sh + SUITE_EXIT_CODE=$? + ;; + checkExtensionScannerRst) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-extensionscanner-rst-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/extensionScannerRstFileReferences.php + SUITE_EXIT_CODE=$? + ;; + checkFilePathLength) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-file-path-length-${SUFFIX} ${IMAGE_PHP} Build/Scripts/maxFilePathLength.sh + SUITE_EXIT_CODE=$? + ;; + checkGitSubmodule) + COMMAND="if [ \$(git submodule status 2>&1 | wc -l) -ne 0 ]; then echo \"Found a submodule definition in repository\"; exit 1; fi" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-git-submodule-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + checkGruntClean) + COMMAND="find 'typo3/sysext' -name '*.js' -not -path '*/Fixtures/*' -exec rm '{}' + && cd Build; npm ci || exit 1; node_modules/grunt/bin/grunt build; cd ..; git add *; git status; git status | grep -q \"nothing to commit, working tree clean\"" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-grunt-clean-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + checkIsoDatabase) + COMMAND="git checkout -- composer.json; git checkout -- composer.lock; php -dxdebug.mode=off Build/Scripts/updateIsoDatabase.php; git add *; git status; git status | grep -q \"nothing to commit, working tree clean\"" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-iso-database-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + checkNamespaceIntegrity) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-namespaces-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/checkNamespaceIntegrity.php + SUITE_EXIT_CODE=$? + ;; + checkPermissions) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-permissions-${SUFFIX} ${IMAGE_PHP} Build/Scripts/checkFilePermissions.sh + SUITE_EXIT_CODE=$? + ;; + checkRst) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name check-rst-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/validateRstFiles.php + SUITE_EXIT_CODE=$? + ;; + clean) + cleanBuildFiles + cleanCacheFiles + cleanRenderedDocumentationFiles + cleanTestFiles + ;; + cleanBuild) + cleanBuildFiles + ;; + cleanCache) + cleanCacheFiles + ;; + cleanRenderedDocumentation) + cleanRenderedDocumentationFiles + ;; + cleanTests) + cleanTestFiles + ;; + composer) + COMMAND=(composer "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + composerInstall) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} composer install --no-progress --no-interaction + SUITE_EXIT_CODE=$? + ;; + composerInstallMax) + COMMAND="composer config --unset platform.php; composer update --no-progress --no-interaction; composer dumpautoload" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-max-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + composerInstallMin) + COMMAND="composer config platform.php ${PHP_VERSION}.0; composer update --prefer-lowest --no-progress --no-interaction; composer dumpautoload" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-install-min-${SUFFIX} -e COMPOSER_CACHE_DIR=.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + composerTestDistribution) + COMMAND="cd Build/composer; rm -rf composer.json composer.lock public/index.php public/typo3 public/typo3conf/ext var/ vendor/; cp composer.dist.json composer.json; composer update --no-progress --no-interaction" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-test-distribution-${SUFFIX} -e COMPOSER_CACHE_DIR=${CORE_ROOT}/.cache/composer -e COMPOSER_ROOT_VERSION=${COMPOSER_ROOT_VERSION} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + composerValidate) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name composer-validate-${SUFFIX} ${IMAGE_PHP} composer validate + SUITE_EXIT_CODE=$? + ;; + functional) + if [ "${CHUNKS}" -gt 0 ]; then + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name func-splitter-${SUFFIX} ${IMAGE_PHP} php -dxdebug.mode=off Build/Scripts/splitFunctionalTests.php -v ${CHUNKS} + COMMAND=(bin/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") + else + COMMAND=(bin/phpunit -c Build/phpunit/FunctionalTests.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") + fi + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name redis-func-${SUFFIX} --network ${NETWORK} -d ${IMAGE_REDIS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name memcached-func-${SUFFIX} --network ${NETWORK} -d ${IMAGE_MEMCACHED} >/dev/null + waitFor redis-func-${SUFFIX} 6379 + waitFor memcached-func-${SUFFIX} 11211 + CONTAINER_COMMON_PARAMS="${CONTAINER_COMMON_PARAMS} -e typo3TestingRedisHost=redis-func-${SUFFIX} -e typo3TestingMemcachedHost=memcached-func-${SUFFIX}" + case ${DBMS} in + mariadb) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + sqlite) + # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/functional-sqlite-dbs/" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${CORE_ROOT}/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + esac + ;; + functionalDeprecated) + COMMAND=(bin/phpunit -c Build/phpunit/FunctionalTestsDeprecated.xml --exclude-group not-${DBMS} ${EXTRA_TEST_OPTIONS} "$@") + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name redis-func-dep-${SUFFIX} --network ${NETWORK} -d ${IMAGE_REDIS} >/dev/null + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name memcached-func-dep-${SUFFIX} --network ${NETWORK} -d ${IMAGE_MEMCACHED} >/dev/null + waitFor redis-func-dep-${SUFFIX} 6379 + waitFor memcached-func-dep-${SUFFIX} 11211 + CONTAINER_COMMON_PARAMS="${CONTAINER_COMMON_PARAMS} -e typo3TestingRedisHost=redis-func-dep-${SUFFIX} -e typo3TestingMemcachedHost=memcached-func-dep-${SUFFIX}" + case ${DBMS} in + mariadb) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mariadb-func-dep-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MARIADB} >/dev/null + waitFor mariadb-func-dep-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mariadb-func-dep-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-deprecated-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + mysql) + echo "Using driver: ${DATABASE_DRIVER}" + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name mysql-func-dep-${SUFFIX} --network ${NETWORK} -d -e MYSQL_ROOT_PASSWORD=funcp --tmpfs /var/lib/mysql/:rw,noexec,nosuid ${IMAGE_MYSQL} >/dev/null + waitFor mysql-func-dep-${SUFFIX} 3306 + CONTAINERPARAMS="-e typo3DatabaseDriver=${DATABASE_DRIVER} -e typo3DatabaseName=func_test -e typo3DatabaseUsername=root -e typo3DatabaseHost=mysql-func-dep-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-deprecated-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + postgres) + ${CONTAINER_BIN} run --rm ${CI_PARAMS} --name postgres-func-dep-${SUFFIX} --network ${NETWORK} -d -e POSTGRES_PASSWORD=funcp -e POSTGRES_USER=funcu --tmpfs /var/lib/postgresql/data:rw,noexec,nosuid ${IMAGE_POSTGRES} >/dev/null + waitFor postgres-func-dep-${SUFFIX} 5432 + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_pgsql -e typo3DatabaseName=bamboo -e typo3DatabaseUsername=funcu -e typo3DatabaseHost=postgres-func-dep-${SUFFIX} -e typo3DatabasePassword=funcp" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-deprecated-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + sqlite) + # create sqlite tmpfs mount typo3temp/var/tests/functional-sqlite-dbs/ to avoid permission issues + mkdir -p "${CORE_ROOT}/typo3temp/var/tests/functional-sqlite-dbs/" + CONTAINERPARAMS="-e typo3DatabaseDriver=pdo_sqlite --tmpfs ${CORE_ROOT}/typo3temp/var/tests/functional-sqlite-dbs/:rw,noexec,nosuid" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name functional-deprecated-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${CONTAINERPARAMS} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + esac + ;; + lintPhp) + COMMAND="php -v | grep '^PHP'; find typo3/ -name \\*.php -print0 | xargs -0 -n1 -P4 php -dxdebug.mode=off -l >/dev/null" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-php-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + lintScss) + COMMAND="cd Build; npm ci || exit 1; node_modules/grunt/bin/grunt stylelint" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-css-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + lintTypescript) + COMMAND="cd Build; npm ci || exit 1; node_modules/grunt/bin/grunt eslint" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-typescript-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + lintHtml) + COMMAND="cd Build; npm ci || exit 1; node_modules/grunt/bin/grunt exec:lintspaces" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name lint-html-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + listExceptionCodes) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name list-exception-codes-${SUFFIX} ${IMAGE_PHP} Build/Scripts/duplicateExceptionCodeCheck.sh -p + SUITE_EXIT_CODE=$? + ;; + npm) + COMMAND=(npm "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} -w ${CORE_ROOT}/Build -e HOME=${CORE_ROOT}/.cache --name npm-${SUFFIX} ${IMAGE_NODEJS} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + phpstan) + COMMAND=(php -dxdebug.mode=off bin/phpstan analyse -c Build/phpstan/${PHPSTAN_CONFIG_FILE} --no-progress --no-interaction --memory-limit 4G "$@") + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-${SUFFIX} ${IMAGE_PHP} "${COMMAND[@]}" + SUITE_EXIT_CODE=$? + ;; + phpstanGenerateBaseline) + COMMAND="php -dxdebug.mode=off bin/phpstan analyse -c Build/phpstan/${PHPSTAN_CONFIG_FILE} --no-progress --no-interaction --memory-limit 4G --generate-baseline=Build/phpstan/phpstan-baseline.neon" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name phpstan-baseline-${SUFFIX} ${IMAGE_PHP} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + unit) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} bin/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} "$@" + SUITE_EXIT_CODE=$? + ;; + unitDeprecated) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-deprecated-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} bin/phpunit -c Build/phpunit/UnitTestsDeprecated.xml ${EXTRA_TEST_OPTIONS} "$@" + SUITE_EXIT_CODE=$? + ;; + unitJavascript) + COMMAND="cd Build; npm ci || exit 1; CHROME_SANDBOX=false BROWSERS=chrome npm run test" + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-javascript-${SUFFIX} -e HOME=${CORE_ROOT}/.cache ${IMAGE_NODEJS_CHROME} /bin/sh -c "${COMMAND}" + SUITE_EXIT_CODE=$? + ;; + unitRandom) + ${CONTAINER_BIN} run ${CONTAINER_COMMON_PARAMS} --name unit-random-${SUFFIX} ${XDEBUG_MODE} -e XDEBUG_CONFIG="${XDEBUG_CONFIG}" ${IMAGE_PHP} bin/phpunit -c Build/phpunit/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} "$@" + SUITE_EXIT_CODE=$? + ;; + update) + # pull typo3/core-testing-* versions of those ones that exist locally + echo "> pull ghcr.io/typo3/core-testing-* versions of those ones that exist locally" + ${CONTAINER_BIN} images "ghcr.io/typo3/core-testing-*" --format "{{.Repository}}:{{.Tag}}" | xargs -I {} ${CONTAINER_BIN} pull {} + echo "" + # remove "dangling" typo3/core-testing-* images (those tagged as ) + echo "> remove \"dangling\" ghcr.io/typo3/core-testing-* images (those tagged as )" + ${CONTAINER_BIN} images --filter "reference=ghcr.io/typo3/core-testing-*" --filter "dangling=true" --format "{{.ID}}" | xargs -I {} ${CONTAINER_BIN} rmi -f {} + echo "" + ;; + *) + loadHelp + echo "Invalid -s option argument ${TEST_SUITE}" >&2 + echo >&2 + echo "${HELP}" >&2 + exit 1 + ;; +esac + +cleanUp + +# Print summary +echo "" >&2 +echo "###########################################################################" >&2 +echo "Result of ${TEST_SUITE}" >&2 +echo "Container runtime: ${CONTAINER_BIN}" >&2 +echo "PHP: ${PHP_VERSION}" >&2 +if [[ ${TEST_SUITE} =~ ^(functional|functionalDeprecated|acceptance|acceptanceComposer|acceptanceInstall)$ ]]; then + case "${DBMS}" in + mariadb|mysql|postgres) + echo "DBMS: ${DBMS} version ${DBMS_VERSION} driver ${DATABASE_DRIVER}" >&2 + ;; + sqlite) + echo "DBMS: ${DBMS}" >&2 + ;; + esac +fi +if [[ -n ${EXTRA_TEST_OPTIONS} ]]; then + echo " Note: Using -e is deprecated. Simply add the options at the end of the command." + echo " Instead of: Build/Scripts/runTests.sh -s ${TEST_SUITE} -e '${EXTRA_TEST_OPTIONS}' $@" + echo " use: Build/Scripts/runTests.sh -s ${TEST_SUITE} -- ${EXTRA_TEST_OPTIONS} $@" +fi +if [[ ${SUITE_EXIT_CODE} -eq 0 ]]; then + echo "SUCCESS" >&2 +else + echo "FAILURE" >&2 +fi +echo "###########################################################################" >&2 +echo "" >&2 + +# Exit with code of test suite - This script return non-zero if the executed test failed. +exit $SUITE_EXIT_CODE diff --git a/Build/Scripts/test.sh b/Build/Scripts/test.sh new file mode 100755 index 0000000..c1fa0cb --- /dev/null +++ b/Build/Scripts/test.sh @@ -0,0 +1,143 @@ +#!/bin/bash + +export NC='\e[0m' +export RED='\e[0;31m' +export GREEN='\e[0;32m' + +THIS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" +cd "$THIS_SCRIPT_DIR" || exit 1 + +################################################# +# Run resource tests. +# Arguments: +# none +################################################# +checkResources () { + echo "#################################################################" >&2 + echo "Checking documentation, TypeScript and Scss files" >&2 + echo "#################################################################" >&2 + + ./additionalTests.sh -s lintXliff + EXIT_CODE_XLIFF=$? + + ./additionalTests.sh -s buildDocumentation + EXIT_CODE_DOCUMENTATION=$? + + echo "#################################################################" >&2 + echo "Checked documentation, TypeScript and Scss files" >&2 + if [[ ${EXIT_CODE_SCSS} -eq 0 ]] && \ + [[ ${EXIT_CODE_TYPESCRIPT} -eq 0 ]] && \ + [[ ${EXIT_CODE_XLIFF} -eq 0 ]] && \ + [[ ${EXIT_CODE_DOCUMENTATION} -eq 0 ]] + then + echo -e "${GREEN}Resources valid${NC}" >&2 + else + echo -e "${RED}Resources invalid${NC}" >&2 + fi + echo "#################################################################" >&2 + echo "" >&2 + + ./additionalTests.sh -s clean +} + +################################################# +# Run test matrix. +# Arguments: +# php version +# typo3 version +# testing framework version +# test path +# prefer lowest +################################################# +runFunctionalTests () { + local PHP_VERSION="${1}" + local TYPO3_VERSION=${2} + local TESTING_FRAMEWORK=${3} + local TEST_PATH=${4} + local PREFER_LOWEST=${5} + + echo "###########################################################################" >&2 + echo " Run unit and/or functional tests with" >&2 + echo " - TYPO3 ${TYPO3_VERSION}" >&2 + echo " - PHP ${PHP_VERSION}">&2 + echo " - Testing framework ${TESTING_FRAMEWORK}">&2 + echo " - Test path ${TEST_PATH}">&2 + echo " - Additional ${PREFER_LOWEST}">&2 + echo "###########################################################################" >&2 + + ./runTests.sh -s cleanTests + + ./additionalTests.sh \ + -p ${PHP_VERSION} \ + -s lintPhp || exit 1 ; \ + EXIT_CODE_LINT=$? + + ./additionalTests.sh \ + -p ${PHP_VERSION} \ + -s composerInstallPackage \ + -q "typo3/cms-core:${TYPO3_VERSION}" \ + -r " ${PREFER_LOWEST}" || exit 1 ; \ + EXIT_CODE_CORE=$? + + ./additionalTests.sh \ + -p ${PHP_VERSION} \ + -s composerInstallPackage \ + -q "typo3/testing-framework:${TESTING_FRAMEWORK}" \ + -r " --dev ${PREFER_LOWEST}" || exit 1 ; \ + EXIT_CODE_FRAMEWORK=$? + + ./runTests.sh \ + -p ${PHP_VERSION} \ + -s composerValidate || exit 1 ; \ + EXIT_CODE_VALIDATE=$? + + echo "###########################################################################" >&2 + echo " Finished unit and/or functional tests with" >&2 + echo " - TYPO3 ${TYPO3_VERSION}" >&2 + echo " - PHP ${PHP_VERSION}">&2 + echo " - Testing framework ${TESTING_FRAMEWORK}">&2 + echo " - Test path ${TEST_PATH}">&2 + echo " - Additional ${PREFER_LOWEST}">&2 + if [[ ${EXIT_CODE_LINT} -eq 0 ]] && \ + [[ ${EXIT_CODE_INSTALL} -eq 0 ]] && \ + [[ ${EXIT_CODE_CORE} -eq 0 ]] && \ + [[ ${EXIT_CODE_FRAMEWORK} -eq 0 ]] && \ + [[ ${EXIT_CODE_VALIDATE} -eq 0 ]] && \ + [[ ${EXIT_CODE_FUNCTIONAL} -eq 0 ]] + then + echo -e "${GREEN}SUCCESS${NC}" >&2 + else + echo -e "${RED}FAILURE${NC}" >&2 + exit 1 + fi + echo "#################################################################" >&2 + echo "" >&2 +} + +################################################# +# Removes all files created by tests. +# Arguments: +# none +################################################# +cleanup () { + ./runTests.sh -s clean + ./additionalTests.sh -s clean + git checkout ../../composer.json +} + +DEBUG_TESTS=false +if [[ $DEBUG_TESTS != true ]]; then + checkResources + + runFunctionalTests "8.2" "^13.0" "dev-main" "Tests/Functional" || exit 1 + runFunctionalTests "8.2" "^13.0" "dev-main" "Tests/Functional" "--prefer-lowest" || exit 1 + runFunctionalTests "8.3" "^13.0" "dev-main" "Tests/Functional" || exit 1 + runFunctionalTests "8.3" "^13.0" "dev-main" "Tests/Functional" "--prefer-lowest" || exit 1 + cleanup +else + cleanup + runFunctionalTests "8.2" "^13.0" "dev-main" "Tests/Functional" || exit 1 + cleanup + # ./runTests.sh -x -p 8.2 -d sqlite -s functional -e "--group selected" Tests/Functional + # ./runTests.sh -x -p 8.2 -d sqlite -s functional Tests/Functional +fi diff --git a/Build/php-cs-fixer/config.php b/Build/php-cs-fixer/config.php new file mode 100644 index 0000000..5e32557 --- /dev/null +++ b/Build/php-cs-fixer/config.php @@ -0,0 +1,116 @@ + Build/Scripts/additionalTests.sh -p 8.3 -s composerInstallPackage -q "typo3/cms-core:[dev-main,13...]" + * > Build/Scripts/runTests.sh -s cgl + * + * Fix your current patch: + * > Build/Scripts/runTests.sh -s cglGit + */ +if (PHP_SAPI !== 'cli') { + die('This script supports command line usage only. Please check your command.'); +} + +// Return a Code Sniffing configuration using +// all sniffers needed for PER +// and additionally: +// - Remove leading slashes in use clauses. +// - PHP single-line arrays should not have trailing comma. +// - Single-line whitespace before closing semicolon are prohibited. +// - Remove unused use statements in the PHP source code +// - Ensure Concatenation to have at least one whitespace around +// - Remove trailing whitespace at the end of blank lines. +return (new \PhpCsFixer\Config()) + ->setParallelConfig(\PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) + ->setFinder( + PhpCsFixer\Finder::create() + ->ignoreVCSIgnored(true) + ->in(realpath(__DIR__ . '/../../')) + ->exclude('bin') + ->exclude('public') + ->exclude('typo3temp') + ->exclude('vendor') + ) + ->setRiskyAllowed(true) + ->setRules([ + '@DoctrineAnnotation' => true, + // @todo: Switch to @PER-CS2.0 once php-cs-fixer's todo list is done: https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues/7247 + '@PER-CS1.0' => true, + 'array_indentation' => true, + 'array_syntax' => ['syntax' => 'short'], + 'cast_spaces' => ['space' => 'none'], + // @todo: Can be dropped once we enable @PER-CS2.0 + 'concat_space' => ['spacing' => 'one'], + 'declare_equal_normalize' => ['space' => 'none'], + 'declare_parentheses' => true, + 'dir_constant' => true, + // @todo: Can be dropped once we enable @PER-CS2.0 + 'function_declaration' => [ + 'closure_fn_spacing' => 'none', + ], + 'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']], + 'type_declaration_spaces' => true, + 'global_namespace_import' => ['import_classes' => false, 'import_constants' => false, 'import_functions' => false], + 'list_syntax' => ['syntax' => 'short'], + // @todo: Can be dropped once we enable @PER-CS2.0 + 'method_argument_space' => true, + 'modernize_strpos' => true, + 'modernize_types_casting' => true, + 'native_function_casing' => true, + 'no_alias_functions' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_blank_lines' => true, + 'no_leading_namespace_whitespace' => true, + 'no_null_property_initialization' => true, + 'no_short_bool_cast' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_superfluous_elseif' => true, + 'no_trailing_comma_in_singleline' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, + 'ordered_imports' => ['imports_order' => ['class', 'function', 'const'], 'sort_algorithm' => 'alpha'], + 'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']], + 'php_unit_mock_short_will_return' => true, + 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], + 'phpdoc_no_access' => true, + 'phpdoc_no_empty_return' => true, + 'phpdoc_no_package' => true, + 'phpdoc_scalar' => true, + 'phpdoc_trim' => true, + 'phpdoc_types' => true, + 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + 'return_type_declaration' => ['space_before' => 'none'], + 'single_quote' => true, + 'single_space_around_construct' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], + // @todo: Can be dropped once we enable @PER-CS2.0 + 'single_line_empty_body' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'whitespace_after_comma_in_array' => ['ensure_single_space' => true], + 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], + ]); diff --git a/Build/php-cs-fixer/header-comment.php b/Build/php-cs-fixer/header-comment.php new file mode 100644 index 0000000..284ba9b --- /dev/null +++ b/Build/php-cs-fixer/header-comment.php @@ -0,0 +1,75 @@ + Build/Scripts/additionalTests.sh -p 8.3 -s composerInstallPackage -q "typo3/cms-core:[dev-main,13...]" + * > Build/Scripts/runTests.sh -s cglHeader + * + * Fix your current patch: + * > Build/Scripts/runTests.sh -s cglHeaderGit + */ +if (PHP_SAPI !== 'cli') { + die('This script supports command line usage only. Please check your command.'); +} + +$finder = PhpCsFixer\Finder::create() + ->name('*.php') + ->in(__DIR__ . '/../../') + ->exclude('Acceptance/Support/_generated') // EXT:core + ->exclude('Build') + // Configuration files do not need header comments + ->exclude('Configuration') + ->notName('*locallang*.php') + ->notName('ext_localconf.php') + ->notName('ext_tables.php') + ->notName('ext_emconf.php') + // ClassAliasMap files do not need header comments + ->notName('ClassAliasMap.php') + // CodeSnippets and Examples in Documentation do not need header comments + ->exclude('Documentation') + // Third-party inclusion files should not have a changed comment + ->notName('Rfc822AddressesParser.php') + ->notName('ClassMapGenerator.php') +; + +$headerComment = <<setParallelConfig(\PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect()) + ->setRiskyAllowed(false) + ->setRules([ + 'no_extra_blank_lines' => true, + 'header_comment' => [ + 'header' => $headerComment, + 'comment_type' => 'comment', + 'separate' => 'both', + 'location' => 'after_declare_strict', + ], + ]) + ->setFinder($finder); diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php index d59a5c8..d27f63b 100755 --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -1,7 +1,5 @@ site) { try { $this->site = $this->siteFinder->getSiteByPageId($pageUid); - } catch (Exception) { + } catch (\Exception) { } } return $this->site; diff --git a/Classes/Services/QueryBuilderHelper.php b/Classes/Services/QueryBuilderHelper.php index efa5f70..aa385fa 100644 --- a/Classes/Services/QueryBuilderHelper.php +++ b/Classes/Services/QueryBuilderHelper.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Evoweb\EwBase\Services; - /* * This file is developed by evoWeb. * @@ -15,6 +13,8 @@ namespace Evoweb\EwBase\Services; * LICENSE.txt file that was distributed with this source code. */ +namespace Evoweb\EwBase\Services; + use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\ParameterType; use TYPO3\CMS\Core\Database\Query\QueryBuilder; @@ -293,7 +293,8 @@ class QueryBuilderHelper ): mixed { if (array_key_exists($paramName, $paramsOrTypes)) { return $paramsOrTypes[$paramName]; - } elseif (array_key_exists(':' . $paramName, $paramsOrTypes)) { + } + if (array_key_exists(':' . $paramName, $paramsOrTypes)) { // Hash keys can be prefixed with a colon for compatibility return $paramsOrTypes[':' . $paramName]; } diff --git a/Classes/ToolbarItems/ReleaseToolbarItem.php b/Classes/ToolbarItems/ReleaseToolbarItem.php index 4f01aa4..097ba9a 100755 --- a/Classes/ToolbarItems/ReleaseToolbarItem.php +++ b/Classes/ToolbarItems/ReleaseToolbarItem.php @@ -1,7 +1,5 @@ resolveContainer = $config['migrationResolveContainer'] ?? []; $this->colPosOffset = $config['migrationColPosOffset'] ?? 0; $this->configuration = $config['migrationMapping'] ?? []; @@ -82,7 +92,6 @@ class GridelementsToContainerService $this->dataHandler->start([], [], $backendUser); } - protected function resolveContainers(string $layout): void { $containers = $this->getGridElementsByLayout($layout); @@ -110,7 +119,7 @@ class GridelementsToContainerService [ 'tx_gridelements_container' => 0, 'colPos' => $container['colPos'], - 'header' => $child['header'] ?: $container['header'] + 'header' => $child['header'] ?: $container['header'], ] ); $this->moveElementAfterElement($child['uid'], $moveAfterThis['uid']); @@ -134,10 +143,9 @@ class GridelementsToContainerService $this->dataHandler->moveRecord(self::TABLE_NAME, $elementToMove, $elementToMoveAfter * -1); } - protected function migrateConfiguredContainer(): void { - array_walk($this->configuration, function($config, $key) { + array_walk($this->configuration, function ($config, $key) { $containers = $this->getGridElementsByLayout((string)$key); foreach ($containers as $container) { $container['pi_flexform'] = $this->flexFormService->convertFlexFormContentToArray( @@ -157,7 +165,7 @@ class GridelementsToContainerService $data = [ 'CType' => $this->getCType($container, $config), - 'tx_gridelements_backend_layout' => '' + 'tx_gridelements_backend_layout' => '', ]; if (isset($config['map']) && is_array($config['map']) && !empty($container['pi_flexform'])) { @@ -205,13 +213,13 @@ class GridelementsToContainerService if (empty($container[$to]) && !empty($value)) { $data[$to] = $value; } - } catch (\throwable) {} + } catch (\throwable) { + } } return $data; } - protected function updateElement(int $uid, array $changes): void { $this->connectionPool diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php index 4b047ba..58c18b2 100755 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -2,6 +2,17 @@ declare(strict_types=1); +/* + * This file is developed by evoWeb. + * + * 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. + */ + namespace Evoweb\EwBase\User; use TYPO3\CMS\Core\Utility\PathUtility; diff --git a/Classes/ViewHelpers/Array/AddViewHelper.php b/Classes/ViewHelpers/Array/AddViewHelper.php index 7bdc507..fed0fcb 100755 --- a/Classes/ViewHelpers/Array/AddViewHelper.php +++ b/Classes/ViewHelpers/Array/AddViewHelper.php @@ -1,7 +1,5 @@ 1) { // glue contains a special type identifier, resolve the actual glue - list ($type, $value) = explode(':', $glue); + [$type, $value] = explode(':', $glue); $glue = match ($type) { 'constant' => constant($value), default => $value, diff --git a/Classes/ViewHelpers/Condition/InArrayViewHelper.php b/Classes/ViewHelpers/Condition/InArrayViewHelper.php index d59e8f6..52390db 100755 --- a/Classes/ViewHelpers/Condition/InArrayViewHelper.php +++ b/Classes/ViewHelpers/Condition/InArrayViewHelper.php @@ -1,14 +1,18 @@ getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); $result['databaseRow']['uid'] = $rowData['rootPageId']; $result['databaseRow']['identifier'] = $result['customData']['siteIdentifier']; - } elseif (in_array($tableName, ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], true)) { + } elseif ( + in_array( + $tableName, + ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], + true + ) + ) { $rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']); try { $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); diff --git a/Classes/Xclass/SiteTcaInline.php b/Classes/Xclass/SiteTcaInline.php index 1119f87..e420715 100755 --- a/Classes/Xclass/SiteTcaInline.php +++ b/Classes/Xclass/SiteTcaInline.php @@ -1,5 +1,16 @@ resolveSiteRelatedChildren($result, $fieldName); if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) { - throw new \RuntimeException('selectorOrUniqueConfiguration not implemented in sites module', 1624313533); + throw new \RuntimeException( + 'selectorOrUniqueConfiguration not implemented in sites module', + 1624313533 + ); } } diff --git a/Configuration/SiteConfiguration/site_usercentrics.php b/Configuration/SiteConfiguration/site_usercentrics.php index 67c84e0..d5fd966 100755 --- a/Configuration/SiteConfiguration/site_usercentrics.php +++ b/Configuration/SiteConfiguration/site_usercentrics.php @@ -34,7 +34,7 @@ return [ 0 => 'CMP 1', 1 => 'main', ], - ] + ], ], ], 'applicationContext' => [ @@ -56,7 +56,7 @@ return [ 0 => '', 1 => true, ], - ] + ], ], ], ], @@ -72,7 +72,7 @@ return [ 'showitem' => ' id, version, --linebreak--, applicationContext, useBlocker, - ' - ] - ] + ', + ], + ], ]; diff --git a/Configuration/TCA/Overrides/pages.php b/Configuration/TCA/Overrides/pages.php index 6a697aa..5a8dda2 100755 --- a/Configuration/TCA/Overrides/pages.php +++ b/Configuration/TCA/Overrides/pages.php @@ -15,14 +15,14 @@ $newColumns = [ 'suggestOptions' => [ 'default' => [ 'additionalSearchFields' => 'header, bodytext', - 'searchWholePhrase' => false - ] + 'searchWholePhrase' => false, + ], ], 'default' => 0, 'behaviour' => [ - 'allowLanguageSynchronization' => true - ] - ] + 'allowLanguageSynchronization' => true, + ], + ], ], ]; diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..95d36a7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,345 @@ +Some icons used in the TYPO3 project are retrieved from the "Silk" icon set of +Mark James, which can be found at http://famfamfam.com/lab/icons/silk/. This +set is distributed under a Creative Commons Attribution 2.5 License. The +license can be found at http://creativecommons.org/licenses/by/2.5/. +--------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/composer.json b/composer.json index ecedfc6..045ecaa 100755 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ } }, "require": { - "typo3/cms-core": "^13.0 || 13.0.x-dev || dev-main", + "typo3/cms-core": "dev-main", "typo3/cms-backend": "*", "typo3/cms-extbase": "*", @@ -35,12 +35,26 @@ "typo3/cms-tstemplate": "*", "typo3/cms-scheduler": "*", - "helhum/typo3-console": "*", + "helhum/typo3-console": ">8.0", "clickstorm/cs-seo": "*" }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.57.1", + "phpunit/phpunit": "^11.0.3", + "typo3/testing-framework": "dev-main" + }, + "minimum-stability": "dev", + "prefer-stable": true, "extra": { "typo3/cms": { "extension-key": "ew_base" } + }, + "config": { + "bin-dir": "bin", + "allow-plugins": { + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true + } } } diff --git a/ext_localconf.php b/ext_localconf.php index cca6d61..d6875da 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -26,10 +26,10 @@ call_user_func(function () { ]; $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteDatabaseEditRow::class] = [ - 'className' => SiteDatabaseEditRow::class + 'className' => SiteDatabaseEditRow::class, ]; $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteTcaInline::class] = [ - 'className' => SiteTcaInline::class + 'className' => SiteTcaInline::class, ]; $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['siteConfiguration'][ From 879adc63ed9add2566e52d3c0803edf690f8092e Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sat, 25 May 2024 16:30:25 +0200 Subject: [PATCH 12/20] Refactor composer.json --- composer.json | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 045ecaa..e90f414 100755 --- a/composer.json +++ b/composer.json @@ -1,9 +1,12 @@ { "name": "evoweb/ew-base", "type": "typo3-cms-extension", - "autoload": { - "psr-4": { - "Evoweb\\EwBase\\": "Classes/" + "license": ["GPL-2.0-or-later"], + "config": { + "bin-dir": "bin", + "allow-plugins": { + "typo3/class-alias-loader": true, + "typo3/cms-composer-installers": true } }, "require": { @@ -50,11 +53,9 @@ "extension-key": "ew_base" } }, - "config": { - "bin-dir": "bin", - "allow-plugins": { - "typo3/class-alias-loader": true, - "typo3/cms-composer-installers": true + "autoload": { + "psr-4": { + "Evoweb\\EwBase\\": "Classes/" } } } From 3f075992ec8610f69b950a214d1caf8208a8bef3 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 26 May 2024 20:41:01 +0200 Subject: [PATCH 13/20] Remove .cache on cleanup build files --- Build/Scripts/additionalTests.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/Build/Scripts/additionalTests.sh b/Build/Scripts/additionalTests.sh index 49fb49a..03de71a 100755 --- a/Build/Scripts/additionalTests.sh +++ b/Build/Scripts/additionalTests.sh @@ -114,6 +114,7 @@ handleDbmsOptions() { cleanBuildFiles() { echo -n "Clean builds ... " rm -rf \ + .cache \ Build/JavaScript \ Build/node_modules \ Documentation-GENERATED-temp From ec08e04c0dce5510acbf54b9f29ff93013921f5e Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 14 Jul 2024 15:49:40 +0200 Subject: [PATCH 14/20] Fix compatibility --- .../Form/FormDataProvider/UsercentricsDatabaseEditRow.php | 5 ++++- Classes/Xclass/SiteDatabaseEditRow.php | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php index f924b44..45e8a6c 100755 --- a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php +++ b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php @@ -16,12 +16,15 @@ declare(strict_types=1); namespace Evoweb\EwBase\Form\FormDataProvider; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow; +use TYPO3\CMS\Core\Configuration\SiteConfiguration; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; -class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow +readonly class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow { + public function __construct(private SiteConfiguration $siteConfiguration) {} + /** * First level of ['customData']['siteData'] to ['databaseRow'] * diff --git a/Classes/Xclass/SiteDatabaseEditRow.php b/Classes/Xclass/SiteDatabaseEditRow.php index 65f3b27..11adff4 100755 --- a/Classes/Xclass/SiteDatabaseEditRow.php +++ b/Classes/Xclass/SiteDatabaseEditRow.php @@ -14,12 +14,15 @@ namespace Evoweb\EwBase\Xclass; use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow; +use TYPO3\CMS\Core\Configuration\SiteConfiguration; use TYPO3\CMS\Core\Exception\SiteNotFoundException; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\Utility\GeneralUtility; -class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow +readonly class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow { + public function __construct(private SiteConfiguration $siteConfiguration) {} + /** * First level of ['customData']['siteData'] to ['databaseRow'] * From 8af2d4f3f7a669a9dd46bec59beec98d0a2159d1 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 14 Jul 2024 16:20:06 +0200 Subject: [PATCH 15/20] Fix parent constructor call --- .../Form/FormDataProvider/UsercentricsDatabaseEditRow.php | 5 ++++- Classes/Xclass/SiteDatabaseEditRow.php | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php index 45e8a6c..b5b33e7 100755 --- a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php +++ b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php @@ -23,7 +23,10 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; readonly class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow { - public function __construct(private SiteConfiguration $siteConfiguration) {} + public function __construct(private SiteConfiguration $siteConfiguration) + { + parent::__construct($this->siteConfiguration); + } /** * First level of ['customData']['siteData'] to ['databaseRow'] diff --git a/Classes/Xclass/SiteDatabaseEditRow.php b/Classes/Xclass/SiteDatabaseEditRow.php index 11adff4..5e2ca2f 100755 --- a/Classes/Xclass/SiteDatabaseEditRow.php +++ b/Classes/Xclass/SiteDatabaseEditRow.php @@ -21,7 +21,10 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; readonly class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow { - public function __construct(private SiteConfiguration $siteConfiguration) {} + public function __construct(private SiteConfiguration $siteConfiguration) + { + parent::__construct($this->siteConfiguration); + } /** * First level of ['customData']['siteData'] to ['databaseRow'] From f8fece3a937948f72e2bd8dbb68f8c902ea382e7 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sun, 14 Jul 2024 16:45:27 +0200 Subject: [PATCH 16/20] Rename site set --- Configuration/Sets/EwBase/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configuration/Sets/EwBase/config.yaml b/Configuration/Sets/EwBase/config.yaml index 81addb3..6969bf8 100644 --- a/Configuration/Sets/EwBase/config.yaml +++ b/Configuration/Sets/EwBase/config.yaml @@ -1,2 +1,2 @@ name: evoweb/ew-base -label: evoWeb base package +label: Site Base From 30f3dd37d0a4adbb15bb76f6840c040afde2e70c Mon Sep 17 00:00:00 2001 From: sebastian Date: Sun, 10 Nov 2024 15:20:20 +0100 Subject: [PATCH 17/20] Require typo3/cms-core as * --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e90f414..51e0956 100755 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ } }, "require": { - "typo3/cms-core": "dev-main", + "typo3/cms-core": "*", "typo3/cms-backend": "*", "typo3/cms-extbase": "*", From 2e75454bb0a2a686b9e0e4140a163c872ec0c056 Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Mon, 11 Nov 2024 21:29:58 +0100 Subject: [PATCH 18/20] [CHORE] Remove old code --- .../UsercentricsDatabaseEditRow.php | 67 ----- .../UsercentricsTcaInline.php | 53 ---- Classes/Hooks/UsercentricsPostRenderHook.php | 109 ------- .../GridelementsToContainerMigration.php | 107 ------- .../GridelementsToContainerService.php | 277 ------------------ Classes/Xclass/SiteDatabaseEditRow.php | 80 ----- Classes/Xclass/SiteTcaInline.php | 46 --- Configuration/Services.yaml | 18 -- .../SiteConfiguration/Overrides/sites.php | 22 -- .../SiteConfiguration/site_usercentrics.php | 78 ----- ext_localconf.php | 36 +-- 11 files changed, 2 insertions(+), 891 deletions(-) delete mode 100755 Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php delete mode 100755 Classes/Form/FormDataProvider/UsercentricsTcaInline.php delete mode 100755 Classes/Hooks/UsercentricsPostRenderHook.php delete mode 100755 Classes/Updates/GridelementsToContainerMigration.php delete mode 100755 Classes/Updates/GridelementsToContainerService.php delete mode 100755 Classes/Xclass/SiteDatabaseEditRow.php delete mode 100755 Classes/Xclass/SiteTcaInline.php delete mode 100755 Configuration/SiteConfiguration/Overrides/sites.php delete mode 100755 Configuration/SiteConfiguration/site_usercentrics.php diff --git a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php b/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php deleted file mode 100755 index b5b33e7..0000000 --- a/Classes/Form/FormDataProvider/UsercentricsDatabaseEditRow.php +++ /dev/null @@ -1,67 +0,0 @@ -siteConfiguration); - } - - /** - * First level of ['customData']['siteData'] to ['databaseRow'] - * - * @throws \RuntimeException - */ - public function addData(array $result): array - { - if ($result['command'] !== 'edit' || $result['tableName'] !== 'site_usercentrics') { - return $result; - } - - $siteFinder = GeneralUtility::makeInstance(SiteFinder::class, $this->siteConfiguration); - - $rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']); - try { - $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); - $parentFieldName = $result['inlineParentFieldName']; - if (!isset($rowData[$parentFieldName])) { - throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092); - } - $rowData = $rowData[$parentFieldName][$result['vanillaUid']]; - $result['databaseRow']['uid'] = $result['vanillaUid']; - } catch (SiteNotFoundException) { - $rowData = []; - } - - foreach ($rowData as $fieldName => $value) { - // Flat values only - databaseRow has no "tree" - if (!is_array($value)) { - $result['databaseRow'][$fieldName] = $value; - } - } - // All "records" are always on pid 0 - $result['databaseRow']['pid'] = 0; - return $result; - } -} diff --git a/Classes/Form/FormDataProvider/UsercentricsTcaInline.php b/Classes/Form/FormDataProvider/UsercentricsTcaInline.php deleted file mode 100755 index 7b6c490..0000000 --- a/Classes/Form/FormDataProvider/UsercentricsTcaInline.php +++ /dev/null @@ -1,53 +0,0 @@ -addInlineFirstPid($result); - foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { - if (!$this->isInlineField($fieldConfig)) { - continue; - } - $childTableName = $fieldConfig['config']['foreign_table'] ?? ''; - if ($childTableName !== 'site_usercentrics') { - continue; - } - $result['processedTca']['columns'][$fieldName]['children'] = []; - $result = $this->resolveSiteRelatedChildren($result, $fieldName); - if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) { - throw new \RuntimeException('selectorOrUniqueConfiguration not implemented in sites module', 1624313533); - } - } - - return $result; - } -} diff --git a/Classes/Hooks/UsercentricsPostRenderHook.php b/Classes/Hooks/UsercentricsPostRenderHook.php deleted file mode 100755 index cb649aa..0000000 --- a/Classes/Hooks/UsercentricsPostRenderHook.php +++ /dev/null @@ -1,109 +0,0 @@ - '', - 'version' => 'loader', - 'useBlocker' => false, - ]; - - protected ?Site $site = null; - - public function __construct(protected SiteFinder $siteFinder) {} - - public function executePostRenderHook(array $params): void - { - $pageUid = $this->getRequest()->getAttribute('frontend.controller')->page['uid'] ?? 0; - if ($pageUid < 1) { - return; - } - - $siteArguments = $this->getCurrentSiteArguments($pageUid); - $id = $siteArguments['id'] ?? ''; - if ($id !== '') { - $extensionConfig = $this->getExtensionConfiguration($pageUid); - - $preload = []; - $userCentrics = []; - - // CMP 1, 2, ... - $version = $siteArguments['version'] ?? ''; - if ($config = $extensionConfig[$version] ?? false) { - $preload[] = $config['preload'] ?? ''; - $userCentrics[] = $config['template'] ?? ''; - } - - // Blocker - if ( - ($siteArguments['useBlocker'] ?? false) - && $config = $extensionConfig['block'] ?? false - ) { - $preload[] = $config['preload'] ?? ''; - $userCentrics[] = $config['template'] ?? ''; - } - - $params['titleTag'] .= str_replace('###ID###', $id, implode(LF, array_merge($preload, $userCentrics))); - } - } - - protected function getCurrentSiteArguments(int $pageUid): array - { - $siteArguments = $this->siteArguments; - - $site = $this->getSiteByPageUid($pageUid); - try { - foreach ($site->getAttribute('usercentrics') as $usercentrics) { - if (str_contains($usercentrics['applicationContext'] ?? '', (string)Environment::getContext())) { - $siteArguments = $usercentrics; - break; - } - } - } catch (\Exception) { - $siteArguments = []; - } - - return is_array($siteArguments) ? $siteArguments : []; - } - - protected function getExtensionConfiguration($pageUid): array - { - $siteSettings = $this->getSiteByPageUid($pageUid)->getSettings(); - $configuration = $siteSettings->has('ew-base') ? $siteSettings->get('ew-base') : []; - return is_array($configuration['userCentrics'] ?? false) ? $configuration['userCentrics'] : []; - } - - protected function getSiteByPageUid(int $pageUid): Site - { - if (!$this->site) { - try { - $this->site = $this->siteFinder->getSiteByPageId($pageUid); - } catch (\Exception) { - } - } - return $this->site; - } - - protected function getRequest(): ServerRequestInterface - { - return $GLOBALS['TYPO3_REQUEST']; - } -} diff --git a/Classes/Updates/GridelementsToContainerMigration.php b/Classes/Updates/GridelementsToContainerMigration.php deleted file mode 100755 index fe30844..0000000 --- a/Classes/Updates/GridelementsToContainerMigration.php +++ /dev/null @@ -1,107 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - public function getTitle(): string - { - return 'Migrate gridelements to container'; - } - - public function getDescription(): string - { - return 'Migrates content elements that are type gridelements to corresponding container elements'; - } - - public function getPrerequisites(): array - { - return [ - DatabaseUpdatedPrerequisite::class, - ]; - } - - public function updateNecessary(): bool - { - return $this->hasRecordsToUpdate(); - } - - public function executeUpdate(): bool - { - $updateSuccessful = true; - try { - $this->service->migrate(); - } catch (\Exception $exception) { - $this->logger->log(LogLevel::ERROR, $exception->getMessage()); - $updateSuccessful = false; - } - - return $updateSuccessful; - } - - protected function hasRecordsToUpdate(): bool - { - return (bool)$this->getPreparedQueryBuilder()->count('uid')->executeQuery()->fetchOne(); - } - - protected function getPreparedQueryBuilder(): QueryBuilder - { - $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - $queryBuilder - ->from(self::TABLE_NAME) - ->where( - $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')) - ); - return $queryBuilder; - } - - protected function getConnectionPool(): ConnectionPool - { - return GeneralUtility::makeInstance(ConnectionPool::class); - } -} diff --git a/Classes/Updates/GridelementsToContainerService.php b/Classes/Updates/GridelementsToContainerService.php deleted file mode 100755 index 0afc636..0000000 --- a/Classes/Updates/GridelementsToContainerService.php +++ /dev/null @@ -1,277 +0,0 @@ - [ - 'CType' => 'three-col-columns', - ], - '140' => [ - 'CType' => [ - 'search' => 'pi_flexform/type', - 'matches' => [ - 0 => 'two-col-columns-11', - 1 => 'two-col-columns-12', - 2 => 'two-col-columns-21', - ], - ], - 'map' => [ - 'pi_flexform/row_class' => 'frame_class', - ] - ], -]; -*/ -class GridelementsToContainerService -{ - private const TABLE_NAME = 'tt_content'; - - protected array $resolveContainer = []; - - protected int $colPosOffset = 0; - - protected array $configuration = []; - - public function __construct( - protected ConnectionPool $connectionPool, - protected DataHandler $dataHandler, - protected FlexFormService $flexFormService, - ) { - $config = & $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; - $this->resolveContainer = $config['migrationResolveContainer'] ?? []; - $this->colPosOffset = $config['migrationColPosOffset'] ?? 0; - $this->configuration = $config['migrationMapping'] ?? []; - } - - public function migrate(): void - { - $this->initializeDataHandler(); - - // Move children out of container and remove container - foreach ($this->resolveContainer as $containerId) { - $this->resolveContainers((string)$containerId); - } - - $this->migrateConfiguredContainer(); - } - - protected function initializeDataHandler(): void - { - $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); - $backendUser->user = [ - 'uid' => 0, - 'admin' => 1, - ]; - $this->dataHandler->start([], [], $backendUser); - } - - protected function resolveContainers(string $layout): void - { - $containers = $this->getGridElementsByLayout($layout); - foreach ($containers as $container) { - $this->processContainerResolve($container); - } - } - - protected function processContainerResolve(array $container): void - { - $children = $this->getGridContainerChildren($container['uid']); - // move first child after container - $moveAfterThis = $container; - foreach ($children as $child) { - [$moveAfterThis, $container] = $this->processContainerResolveChild($child, $moveAfterThis, $container); - } - - $this->deleteElement($container['uid']); - } - - protected function processContainerResolveChild(array $child, array $moveAfterThis, array $container): array - { - $this->updateElement( - $child['uid'], - [ - 'tx_gridelements_container' => 0, - 'colPos' => $container['colPos'], - 'header' => $child['header'] ?: $container['header'], - ] - ); - $this->moveElementAfterElement($child['uid'], $moveAfterThis['uid']); - // use this child to move the next child after - $moveAfterThis = $child; - // empty container header so only the first child gets the header - $container['header'] = ''; - - return [$moveAfterThis, $container]; - } - - protected function deleteElement(int $uid): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, ['delete' => 1], ['uid' => $uid]); - } - - protected function moveElementAfterElement(int $elementToMove, int $elementToMoveAfter): void - { - $this->dataHandler->moveRecord(self::TABLE_NAME, $elementToMove, $elementToMoveAfter * -1); - } - - protected function migrateConfiguredContainer(): void - { - array_walk($this->configuration, function ($config, $key) { - $containers = $this->getGridElementsByLayout((string)$key); - foreach ($containers as $container) { - $container['pi_flexform'] = $this->flexFormService->convertFlexFormContentToArray( - $container['pi_flexform'] - ); - $this->processContainerMigration($container, $config); - } - }); - } - - protected function processContainerMigration(array $container, array $config): void - { - $children = $this->getGridContainerChildren($container['uid']); - foreach ($children as $child) { - $this->processContainerMigrationChild($child, $container); - } - - $data = [ - 'CType' => $this->getCType($container, $config), - 'tx_gridelements_backend_layout' => '', - ]; - - if (isset($config['map']) && is_array($config['map']) && !empty($container['pi_flexform'])) { - $data = $this->addMappedValues($data, $container, $config['map']); - } - - $this->updateElement($container['uid'], $data); - } - - protected function processContainerMigrationChild(array $child, array $container): void - { - $this->updateElement( - $child['uid'], - [ - 'hidden' => $child['hidden'] ?: $container['hidden'], - 'colPos' => $child['tx_gridelements_columns'] + $this->colPosOffset, - 'tx_container_parent' => $child['tx_gridelements_container'], - 'tx_gridelements_columns' => 0, - 'tx_gridelements_container' => 0, - ] - ); - } - - protected function getCType(array $container, array $config): string - { - if (is_array($config['CType'])) { - $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); - $result = $config['CType']['matches'][$value] ?? null; - } else { - $result = $config['CType'] ?? null; - } - - if (empty($result)) { - throw new \Exception('CType must always be set'); - } - - return $result; - } - - protected function addMappedValues(array $data, array $container, array $config): array - { - foreach ($config as $from => $to) { - try { - $value = ArrayUtility::getValueByPath($container, $from); - if (empty($container[$to]) && !empty($value)) { - $data[$to] = $value; - } - } catch (\throwable) { - } - } - - return $data; - } - - protected function updateElement(int $uid, array $changes): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); - } - - protected function getGridContainerChildren(int $containerUid): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - - return $queryBuilder - ->select('*') - ->where( - $queryBuilder->expr()->eq( - 'tx_gridelements_container', - $queryBuilder->createNamedParameter($containerUid, ParameterType::INTEGER) - ) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getGridElementsByLayout(string $layout): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - $expr = $queryBuilder->expr(); - - return $queryBuilder - ->select('*') - ->where( - $expr->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')), - $expr->eq('tx_gridelements_backend_layout', $queryBuilder->createNamedParameter($layout)) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($table); - - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - - $queryBuilder->from($table); - - return $queryBuilder; - } -} diff --git a/Classes/Xclass/SiteDatabaseEditRow.php b/Classes/Xclass/SiteDatabaseEditRow.php deleted file mode 100755 index 5e2ca2f..0000000 --- a/Classes/Xclass/SiteDatabaseEditRow.php +++ /dev/null @@ -1,80 +0,0 @@ -siteConfiguration); - } - - /** - * First level of ['customData']['siteData'] to ['databaseRow'] - * - * @throws \RuntimeException - */ - public function addData(array $result): array - { - if ($result['command'] !== 'edit' || !empty($result['databaseRow'])) { - return $result; - } - - $tableName = $result['tableName']; - $siteFinder = GeneralUtility::makeInstance(SiteFinder::class, $this->siteConfiguration); - if ($tableName === 'site') { - $rootPageId = (int)$result['vanillaUid']; - $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); - $result['databaseRow']['uid'] = $rowData['rootPageId']; - $result['databaseRow']['identifier'] = $result['customData']['siteIdentifier']; - } elseif ( - in_array( - $tableName, - ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], - true - ) - ) { - $rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']); - try { - $rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId); - $parentFieldName = $result['inlineParentFieldName']; - if (!isset($rowData[$parentFieldName])) { - throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092); - } - $rowData = $rowData[$parentFieldName][$result['vanillaUid']]; - $result['databaseRow']['uid'] = $result['vanillaUid']; - } catch (SiteNotFoundException) { - $rowData = []; - } - } else { - return $result; - } - - foreach ($rowData as $fieldName => $value) { - // Flat values only - databaseRow has no "tree" - if (!is_array($value)) { - $result['databaseRow'][$fieldName] = $value; - } - } - // All "records" are always on pid 0 - $result['databaseRow']['pid'] = 0; - return $result; - } -} diff --git a/Classes/Xclass/SiteTcaInline.php b/Classes/Xclass/SiteTcaInline.php deleted file mode 100755 index e420715..0000000 --- a/Classes/Xclass/SiteTcaInline.php +++ /dev/null @@ -1,46 +0,0 @@ -addInlineFirstPid($result); - foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { - if (!$this->isInlineField($fieldConfig)) { - continue; - } - $childTableName = $fieldConfig['config']['foreign_table'] ?? ''; - if (!in_array($childTableName, ['site_errorhandling', 'site_route', 'site_base_variant'], true)) { - return $result; - } - $result['processedTca']['columns'][$fieldName]['children'] = []; - $result = $this->resolveSiteRelatedChildren($result, $fieldName); - if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) { - throw new \RuntimeException( - 'selectorOrUniqueConfiguration not implemented in sites module', - 1624313533 - ); - } - } - - return $result; - } -} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index b684568..87caa87 100755 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -16,21 +16,3 @@ services: Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem: tags: ['backend.toolbar.item'] - - Evoweb\EwBase\Hooks\UsercentricsPostRenderHook: - public: true - - Evoweb\EwBase\Updates\GridelementsToContainerMigration: - public: true - - Evoweb\EwBase\Updates\GridelementsToContainerService: - public: true - - Evoweb\EwBase\Updates\ParentChildToContainerMigration: - public: true - - Evoweb\EwBase\Updates\ParentChildToContainerService: - public: true - - Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow: - public: true diff --git a/Configuration/SiteConfiguration/Overrides/sites.php b/Configuration/SiteConfiguration/Overrides/sites.php deleted file mode 100755 index 3ec61c2..0000000 --- a/Configuration/SiteConfiguration/Overrides/sites.php +++ /dev/null @@ -1,22 +0,0 @@ - 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site.usercentrics', - 'description' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site.usercentrics.description', - 'config' => [ - 'type' => 'inline', - 'foreign_table' => 'site_usercentrics', - 'maxitems' => 1, - 'appearance' => [ - 'enabledControls' => [ - 'info' => false, - ], - ], - ], -]; - -$GLOBALS['SiteConfiguration']['site']['types']['0']['showitem'] = str_replace( - ' routes', - ' routes, usercentrics,', - $GLOBALS['SiteConfiguration']['site']['types']['0']['showitem'] -); diff --git a/Configuration/SiteConfiguration/site_usercentrics.php b/Configuration/SiteConfiguration/site_usercentrics.php deleted file mode 100755 index d5fd966..0000000 --- a/Configuration/SiteConfiguration/site_usercentrics.php +++ /dev/null @@ -1,78 +0,0 @@ - [ - 'label' => 'id', - 'title' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.ctrl.title', - 'typeicon_classes' => [ - 'default' => 'ew-usercentrics', - ], - ], - 'columns' => [ - 'id' => [ - 'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.id', - 'config' => [ - 'type' => 'input', - 'size' => 12, - 'max' => 12, - ], - ], - 'version' => [ - 'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.version', - 'config' => [ - 'type' => 'select', - 'renderType' => 'selectSingle', - 'minitems' => 0, - 'maxitems' => 1, - 'size' => 1, - 'items' => [ - [ - 0 => 'CMP 2', - 1 => 'loader', - ], - [ - 0 => 'CMP 1', - 1 => 'main', - ], - ], - ], - ], - 'applicationContext' => [ - 'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.applicationContext', - 'description' => 'Values are separated by line breaks', - 'config' => [ - 'type' => 'text', - 'eval' => 'trim', - ], - ], - 'useBlocker' => [ - 'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.useBlocker', - 'config' => [ - 'type' => 'check', - 'renderType' => 'checkboxToggle', - 'default' => false, - 'items' => [ - [ - 0 => '', - 1 => true, - ], - ], - ], - ], - ], - 'types' => [ - '1' => [ - 'showitem' => ' - --palette--;;usercentrics, - ', - ], - ], - 'palettes' => [ - 'usercentrics' => [ - 'showitem' => ' - id, version, --linebreak--, - applicationContext, useBlocker, - ', - ], - ], -]; diff --git a/ext_localconf.php b/ext_localconf.php index d6875da..90c27cd 100755 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -3,45 +3,13 @@ defined('TYPO3') or die('access denied'); use Evoweb\EwBase\Form\Element\PickColorFromImage; -use Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow; -use Evoweb\EwBase\Form\FormDataProvider\UsercentricsTcaInline; -use Evoweb\EwBase\Hooks\UsercentricsPostRenderHook; -use Evoweb\EwBase\Xclass\SiteDatabaseEditRow; -use Evoweb\EwBase\Xclass\SiteTcaInline; -use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseParentPageRow; -use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow; -use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline as BaseSiteTcaInline; -use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage; -call_user_func(function () { +(static function () { $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['ewb'] = [ 'Evoweb\\EwBase\\ViewHelpers' ]; - $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'][] = - UsercentricsPostRenderHook::class . '->executePostRenderHook'; - $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [ 'nodeName' => 'pick-color-from-image', 'priority' => '70', 'class' => PickColorFromImage::class, ]; - - $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteDatabaseEditRow::class] = [ - 'className' => SiteDatabaseEditRow::class, - ]; - $GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteTcaInline::class] = [ - 'className' => SiteTcaInline::class, - ]; - - $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['siteConfiguration'][ - UsercentricsDatabaseEditRow::class - ] = [ - 'depends' => [ BaseSiteDatabaseEditRow::class ], - 'before' => [ DatabaseParentPageRow::class ], - ]; - $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['siteConfiguration'][ - UsercentricsTcaInline::class - ] = [ - 'depends' => [ BaseSiteTcaInline::class ], - 'before' => [ TcaSiteLanguage::class ], - ]; -}); +})(); From 47e55d8f487bde6b4e8eef05199779fd87ef344f Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Sat, 14 Dec 2024 14:03:20 +0100 Subject: [PATCH 19/20] [TASK] Update base extension --- .gitlab-ci.yml | 0 Classes/Command/ContentElementCommand.php | 13 +- .../Configuration/AdditionalConfiguration.php | 55 +- Classes/DataProcessing/ContainerProcessor.php | 58 ++ .../DataProcessing/DatabaseQueryProcessor.php | 831 ++++++++++++++++++ Classes/EventListener/CssMerger.php | 18 +- .../IsContentUsedOnPageLayout.php | 25 + Classes/EventListener/JsMerger.php | 13 - Classes/Form/Element/PickColorFromImage.php | 67 +- Classes/Hooks/PageLayoutView.php | 34 + Classes/ToolbarItems/ReleaseToolbarItem.php | 9 +- .../GridelementsToContainerMigration.php | 96 ++ .../GridelementsToContainerService.php | 268 ++++++ .../ParentChildToContainerMigration.php | 106 +++ .../Updates/ParentChildToContainerService.php | 168 ++++ Classes/User/AssetPath.php | 16 +- Classes/ViewHelpers/Array/AddViewHelper.php | 15 +- .../ViewHelpers/Be/ThumbnailViewHelper.php | 25 +- .../Condition/InArrayViewHelper.php | 23 +- .../Condition/StringContainsViewHelper.php | 25 +- .../Context/DevelopmentViewHelper.php | 18 +- .../Context/ProductionViewHelper.php | 18 +- .../ViewHelpers/Context/StagingViewHelper.php | 20 +- .../FixFlexformForExtbaseViewHelper.php | 77 ++ Classes/ViewHelpers/FlexFormViewHelper.php | 80 ++ Classes/ViewHelpers/HashViewHelper.php | 24 +- .../ViewHelpers/Iterator/AddViewHelper.php | 56 ++ .../Iterator/ExplodeViewHelper.php | 93 ++ Classes/ViewHelpers/PublicPathViewHelper.php | 26 +- Classes/ViewHelpers/ReplaceViewHelper.php | 14 +- Classes/ViewHelpers/SvgViewHelper.php | 63 +- Classes/ViewHelpers/TrimViewHelper.php | 21 +- Configuration/Icons.php | 0 Configuration/JavaScriptModules.php | 0 Configuration/Services.yaml | 27 + Configuration/TCA/Overrides/pages.php | 10 +- Configuration/Yaml/Csp/Cookiebot.yaml | 0 .../Yaml/Csp/GoogleAdsConversions.yaml | 0 .../Yaml/Csp/GoogleAdsRemarketing.yaml | 0 Configuration/Yaml/Csp/GoogleAnalytics4.yaml | 0 .../Yaml/Csp/GoogleAnalytics4Signals.yaml | 0 .../Yaml/Csp/GoogleAnalytics4SignalsEu.yaml | 0 Configuration/Yaml/Csp/GoogleOptimize.yaml | 0 .../Yaml/Csp/GoogleTagManagerPreview.yaml | 0 Configuration/Yaml/Csp/MapBox.yaml | 0 .../Yaml/Csp/UniversalAnalytics.yaml | 0 Configuration/Yaml/Csp/Youtube.yaml | 0 README.md | 0 Resources/Private/Language/locallang_core.xlf | 0 Resources/Private/Language/locallang_db.xlf | 0 .../Language/locallang_siteconfiguration.xlf | 0 .../Form/ImageManipulationElement.html | 0 .../ToolbarItems/ShowReleaseDropDown.html | 4 +- .../ToolbarItems/ShowReleaseToolbarItem.html | 0 Resources/Public/Icons/Extension.svg | 0 Resources/Public/Icons/Extension_16.svg | 0 Resources/Public/Icons/usercentrics.svg | 0 .../element/pick-color-from-image.css | 0 .../element/pick-color-from-image.js | 0 composer.json | 24 +- ext_localconf.php | 2 +- ext_tables.sql | 0 62 files changed, 2152 insertions(+), 290 deletions(-) mode change 100755 => 100644 .gitlab-ci.yml mode change 100755 => 100644 Classes/Command/ContentElementCommand.php mode change 100755 => 100644 Classes/Configuration/AdditionalConfiguration.php create mode 100644 Classes/DataProcessing/ContainerProcessor.php create mode 100644 Classes/DataProcessing/DatabaseQueryProcessor.php mode change 100755 => 100644 Classes/EventListener/CssMerger.php create mode 100644 Classes/EventListener/IsContentUsedOnPageLayout.php mode change 100755 => 100644 Classes/EventListener/JsMerger.php mode change 100755 => 100644 Classes/Form/Element/PickColorFromImage.php create mode 100644 Classes/Hooks/PageLayoutView.php mode change 100755 => 100644 Classes/ToolbarItems/ReleaseToolbarItem.php create mode 100644 Classes/Updates/GridelementsToContainerMigration.php create mode 100644 Classes/Updates/GridelementsToContainerService.php create mode 100644 Classes/Updates/ParentChildToContainerMigration.php create mode 100644 Classes/Updates/ParentChildToContainerService.php mode change 100755 => 100644 Classes/User/AssetPath.php mode change 100755 => 100644 Classes/ViewHelpers/Array/AddViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Be/ThumbnailViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Condition/InArrayViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Condition/StringContainsViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Context/DevelopmentViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Context/ProductionViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/Context/StagingViewHelper.php create mode 100644 Classes/ViewHelpers/FixFlexformForExtbaseViewHelper.php create mode 100644 Classes/ViewHelpers/FlexFormViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/HashViewHelper.php create mode 100644 Classes/ViewHelpers/Iterator/AddViewHelper.php create mode 100644 Classes/ViewHelpers/Iterator/ExplodeViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/PublicPathViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/ReplaceViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/SvgViewHelper.php mode change 100755 => 100644 Classes/ViewHelpers/TrimViewHelper.php mode change 100755 => 100644 Configuration/Icons.php mode change 100755 => 100644 Configuration/JavaScriptModules.php mode change 100755 => 100644 Configuration/Services.yaml mode change 100755 => 100644 Configuration/TCA/Overrides/pages.php mode change 100755 => 100644 Configuration/Yaml/Csp/Cookiebot.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleAdsConversions.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleAdsRemarketing.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleAnalytics4.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleAnalytics4Signals.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleAnalytics4SignalsEu.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleOptimize.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/GoogleTagManagerPreview.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/MapBox.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/UniversalAnalytics.yaml mode change 100755 => 100644 Configuration/Yaml/Csp/Youtube.yaml mode change 100755 => 100644 README.md mode change 100755 => 100644 Resources/Private/Language/locallang_core.xlf mode change 100755 => 100644 Resources/Private/Language/locallang_db.xlf mode change 100755 => 100644 Resources/Private/Language/locallang_siteconfiguration.xlf mode change 100755 => 100644 Resources/Private/Templates/Form/ImageManipulationElement.html mode change 100755 => 100644 Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html mode change 100755 => 100644 Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html mode change 100755 => 100644 Resources/Public/Icons/Extension.svg mode change 100755 => 100644 Resources/Public/Icons/Extension_16.svg mode change 100755 => 100644 Resources/Public/Icons/usercentrics.svg mode change 100755 => 100644 Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css mode change 100755 => 100644 Resources/Public/JavaScript/form-engine/element/pick-color-from-image.js mode change 100755 => 100644 composer.json mode change 100755 => 100644 ext_localconf.php mode change 100755 => 100644 ext_tables.sql diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml old mode 100755 new mode 100644 diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php old mode 100755 new mode 100644 index d27f63b..687e76d --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -1,5 +1,7 @@ connectionPool = $connectionPool; parent::__construct(); } - protected function configure(): void + protected function configure() { $this - ->setAliases(['ce']) + ->setAliases(['kc-sitepackage']) ->addOption( 'pageId', '-p', diff --git a/Classes/Configuration/AdditionalConfiguration.php b/Classes/Configuration/AdditionalConfiguration.php old mode 100755 new mode 100644 index 540cbaa..e6b6165 --- a/Classes/Configuration/AdditionalConfiguration.php +++ b/Classes/Configuration/AdditionalConfiguration.php @@ -2,19 +2,18 @@ declare(strict_types=1); +namespace Evoweb\EwBase\Configuration; + /* - * This file is developed by evoWeb. + * This file is part of TYPO3 CMS-based extension "container" by b13. * * 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. */ -namespace Evoweb\EwBase\Configuration; - +use Mfc\OAuth2\ResourceServer\GitLab; +use Mfc\OAuth2\ResourceServer\Registry; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -23,6 +22,24 @@ class AdditionalConfiguration { protected string $extensionKey = 'ew_base'; + protected array $oauthOptions = [ + 'enabled' => true, // Enable/Disable the provider + 'arguments' => [ + 'appId' => '', + 'appSecret' => '', + 'projectName' => '', + 'gitlabServer' => 'https://github.com', + // User level at which the user will be given admin permissions + 'gitlabAdminUserLevel' => 30, + // Groups to assign to the User (comma separated list possible) + 'gitlabDefaultGroups' => 1, + // UserConfig db and/or file mount from groups + 'gitlabUserOption' => 3, + // Blocks users with flag external from access the backend + 'blockExternalUser' => false, + ], + ]; + protected array $developConfig = [ 'BE' => [ 'debug' => true, @@ -57,8 +74,7 @@ class AdditionalConfiguration */ protected array $mailConfig = [ 'transport' => 'smtp', - 'transport_smtp_server' => '127.0.0.1:1025', - 'defaultMailFromAddress' => 'test@dev.arpa', + 'transport_smtp_server' => '127.0.0.1:1025' ]; public function initialize(array $configuration = []): void @@ -66,7 +82,6 @@ class AdditionalConfiguration $this->addContextToSitename(); $this->addContextConfiguration($configuration); if (Environment::getContext() == 'Development') { - $this->addBaseUrl(); $this->addDebugConfiguration(); } $this->addFurtherConfigurationFiles(); @@ -77,28 +92,6 @@ class AdditionalConfiguration $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] .= ' - ' . Environment::getContext(); } - /** - * Set the baseurl on local environments automatically - */ - protected function addBaseUrl(): void - { - if (Environment::isCli()) { - return; - } - $remoteHost = GeneralUtility::getIndpEnv('HTTP_HOST'); - ExtensionManagementUtility::addTypoScript( - $this->extensionKey, - 'constants', - ' - // condition should trigger different cache hashes - [request && request.getNormalizedParams().getHttpHost() == \'' . $remoteHost . '\'] - config.baseURL = ' . $remoteHost . ' - [end] - ', - 'defaultContentRendering' - ); - } - protected function addDebugConfiguration(): void { $GLOBALS['TYPO3_CONF_VARS'] = $this->arrayMergeRecursive( diff --git a/Classes/DataProcessing/ContainerProcessor.php b/Classes/DataProcessing/ContainerProcessor.php new file mode 100644 index 0000000..6d48d2a --- /dev/null +++ b/Classes/DataProcessing/ContainerProcessor.php @@ -0,0 +1,58 @@ +getChildrenByColPos($colPos); + + if (!$processorConfiguration['doNotProcessChildren']) { + $contentRecordRenderer = new RecordsContentObject(); + $conf = [ + 'tables' => 'tt_content' + ]; + foreach ($children as &$child) { + if ($child['l18n_parent'] > 0) { + $conf['source'] = $child['l18n_parent']; + } else { + $conf['source'] = $child['uid']; + } + if ($child['t3ver_oid'] > 0) { + $conf['source'] = $child['t3ver_oid']; + } + $child['renderedContent'] = $cObj->render($contentRecordRenderer, $conf); + /** @var ContentObjectRenderer $recordContentObjectRenderer */ + $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); + $recordContentObjectRenderer->start($child, 'tt_content'); + $child = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $child); + } + } + + $processedData[$as] = $children; + return $processedData; + } +} diff --git a/Classes/DataProcessing/DatabaseQueryProcessor.php b/Classes/DataProcessing/DatabaseQueryProcessor.php new file mode 100644 index 0000000..057ec66 --- /dev/null +++ b/Classes/DataProcessing/DatabaseQueryProcessor.php @@ -0,0 +1,831 @@ +contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class); + } + + /** + * Fetches records from the database as an array + * + * @param ContentObjectRenderer $cObj The data of the content element or page + * @param array $contentObjectConfiguration The configuration of Content Object + * @param array $processorConfiguration The configuration of this processor + * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) + * + * @return array the processed data as key/value store + */ + public function process( + ContentObjectRenderer $cObj, + array $contentObjectConfiguration, + array $processorConfiguration, + array $processedData + ) { + $this->cObj = $cObj; + if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { + return $processedData; + } + + // the table to query, if none given, exit + $tableName = $cObj->stdWrapValue('table', $processorConfiguration); + if (empty($tableName)) { + return $processedData; + } + if (isset($processorConfiguration['table.'])) { + unset($processorConfiguration['table.']); + } + if (isset($processorConfiguration['table'])) { + unset($processorConfiguration['table']); + } + + // The variable to be used within the result + $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'records'); + + // Execute a SQL statement to fetch the records + $records = $this->getRecords($tableName, $processorConfiguration); + $request = $cObj->getRequest(); + $processedRecordVariables = []; + foreach ($records as $key => $record) { + $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); + $recordContentObjectRenderer->start($record, $tableName, $request); + $processedRecordVariables[$key] = ['data' => $record]; + $processedRecordVariables[$key] = $this->contentDataProcessor->process( + $recordContentObjectRenderer, + $processorConfiguration, + $processedRecordVariables[$key] + ); + } + + $processedData[$targetVariableName] = $processedRecordVariables; + + return $processedData; + } + + protected function getRecords(string $tableName, array $queryConfiguration): array + { + $records = []; + + $statement = $this->exec_getQuery($tableName, $queryConfiguration); + + $tsfe = $this->getTypoScriptFrontendController(); + while ($row = $statement->fetchAssociative()) { + // Versioning preview: + $tsfe->sys_page->versionOL($tableName, $row, true); + + // Language overlay: + if (is_array($row)) { + $row = $tsfe->sys_page->getLanguageOverlay($tableName, $row); + } + + // Might be unset in the language overlay + if (is_array($row)) { + $records[] = $row; + } + } + + return $records; + } + + protected function exec_getQuery(string $table, array $conf): Result + { + $statement = $this->getQuery($table, $conf); + $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + + return $connection->executeQuery($statement); + } + + public function getQuery($table, $conf, $returnQueryArray = false) + { + // Resolve stdWrap in these properties first + $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $properties = [ + 'pidInList', + 'uidInList', + 'languageField', + 'selectFields', + 'max', + 'begin', + 'groupBy', + 'orderBy', + 'join', + 'leftjoin', + 'rightjoin', + 'recursive', + 'where', + ]; + foreach ($properties as $property) { + $conf[$property] = trim( + isset($conf[$property . '.']) + ? (string)$this->cObj->stdWrap($conf[$property] ?? '', $conf[$property . '.'] ?? []) + : (string)($conf[$property] ?? '') + ); + if ($conf[$property] === '') { + unset($conf[$property]); + } elseif (in_array($property, ['languageField', 'selectFields', 'join', 'leftjoin', 'rightjoin', 'where'], true)) { + $conf[$property] = QueryHelper::quoteDatabaseIdentifiers($connection, $conf[$property]); + } + if (isset($conf[$property . '.'])) { + // stdWrapping already done, so remove the sub-array + unset($conf[$property . '.']); + } + } + // Handle PDO-style named parameter markers first + $queryMarkers = $this->getQueryMarkers($table, $conf); + // Replace the markers in the non-stdWrap properties + foreach ($queryMarkers as $marker => $markerValue) { + $properties = [ + 'uidInList', + 'selectFields', + 'where', + 'max', + 'begin', + 'groupBy', + 'orderBy', + 'join', + 'leftjoin', + 'rightjoin', + ]; + foreach ($properties as $property) { + if ($conf[$property] ?? false) { + $conf[$property] = str_replace('###' . $marker . '###', (string)$markerValue, $conf[$property]); + } + } + } + + // Construct WHERE clause: + // Handle recursive function for the pidInList + if (isset($conf['recursive'])) { + $conf['recursive'] = (int)$conf['recursive']; + if ($conf['recursive'] > 0) { + $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true); + array_walk($pidList, function (&$storagePid) { + if ($storagePid === 'this') { + $storagePid = $this->getTypoScriptFrontendController()->id; + } + }); + $expandedPidList = $this->getTypoScriptFrontendController()->sys_page->getPageIdsRecursive($pidList, $conf['recursive']); + $conf['pidInList'] = implode(',', $expandedPidList); + } + } + if ((string)($conf['pidInList'] ?? '') === '') { + $conf['pidInList'] = 'this'; + } + + $queryParts = $this->getQueryConstraints($table, $conf); + + $queryBuilder = $connection->createQueryBuilder(); + // @todo Check against getQueryConstraints, can probably use FrontendRestrictions + // @todo here and remove enableFields there. + $queryBuilder->getRestrictions()->removeAll(); + $queryBuilder->select('*')->from($table); + + if ($queryParts['where'] ?? false) { + $queryBuilder->where($queryParts['where']); + } + + if ($queryParts['groupBy'] ?? false) { + $queryBuilder->groupBy(...$queryParts['groupBy']); + } + + if (is_array($queryParts['orderBy'] ?? false)) { + foreach ($queryParts['orderBy'] as $orderBy) { + $queryBuilder->addOrderBy(...$orderBy); + } + } + + // Fields: + if ($conf['selectFields'] ?? false) { + $queryBuilder->selectLiteral($this->sanitizeSelectPart($conf['selectFields'], $table)); + } + + // Setting LIMIT: + $error = false; + if (($conf['max'] ?? false) || ($conf['begin'] ?? false)) { + // Finding the total number of records, if used: + if (str_contains(strtolower(($conf['begin'] ?? '') . ($conf['max'] ?? '')), 'total')) { + $countQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $countQueryBuilder->getRestrictions()->removeAll(); + $countQueryBuilder->count('*') + ->from($table) + ->where($queryParts['where']); + + if ($queryParts['groupBy']) { + $countQueryBuilder->groupBy(...$queryParts['groupBy']); + } + + try { + $count = $countQueryBuilder->executeQuery()->fetchOne(); + if (isset($conf['max'])) { + $conf['max'] = str_ireplace('total', $count, (string)$conf['max']); + } + if (isset($conf['begin'])) { + $conf['begin'] = str_ireplace('total', $count, (string)$conf['begin']); + } + } catch (DBALException $e) { + $this->getTimeTracker()->setTSlogMessage($e->getPrevious()->getMessage()); + $error = true; + } + } + + if (!$error) { + if (isset($conf['begin']) && $conf['begin'] > 0) { + $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['begin'])), 0); + $queryBuilder->setFirstResult($conf['begin']); + } + if (isset($conf['max'])) { + $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['max'])), 0); + $queryBuilder->setMaxResults($conf['max'] ?: 100000); + } + } + } + + if (!$error) { + // Setting up tablejoins: + if ($conf['join'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['join']); + $queryBuilder->join( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['leftjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['leftjoin']); + $queryBuilder->leftJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['rightjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['rightjoin']); + $queryBuilder->rightJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } + + // Convert the QueryBuilder object into a SQL statement. + $query = $queryBuilder->getSQL(); + + // Replace the markers in the queryParts to handle stdWrap enabled properties + foreach ($queryMarkers as $marker => $markerValue) { + // @todo Ugly hack that needs to be cleaned up, with the current architecture + // @todo for exec_Query / getQuery it's the best we can do. + $query = str_replace('###' . $marker . '###', (string)$markerValue, $query); + } + + return $returnQueryArray ? $this->getQueryArray($queryBuilder) : $query; + } + + return ''; + } + + /** + * Helper to transform a QueryBuilder object into a queryParts array that can be used + * with exec_SELECT_queryArray + * + * @return array + * @throws \RuntimeException + */ + protected function getQueryArray(QueryBuilder $queryBuilder) + { + $fromClauses = []; + $knownAliases = []; + $queryParts = []; + + // Loop through all FROM clauses + foreach ($queryBuilder->getQueryPart('from') as $from) { + if ($from['alias'] === null) { + $tableSql = $from['table']; + $tableReference = $from['table']; + } else { + $tableSql = $from['table'] . ' ' . $from['alias']; + $tableReference = $from['alias']; + } + + $knownAliases[$tableReference] = true; + + $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper( + $tableReference, + $queryBuilder->getQueryPart('join'), + $knownAliases + ); + } + + $queryParts['SELECT'] = implode(', ', $queryBuilder->getQueryPart('select')); + $queryParts['FROM'] = implode(', ', $fromClauses); + $queryParts['WHERE'] = (string)$queryBuilder->getQueryPart('where') ?: ''; + $queryParts['GROUPBY'] = implode(', ', $queryBuilder->getQueryPart('groupBy')); + $queryParts['ORDERBY'] = implode(', ', $queryBuilder->getQueryPart('orderBy')); + if ($queryBuilder->getFirstResult() > 0) { + $queryParts['LIMIT'] = $queryBuilder->getFirstResult() . ',' . $queryBuilder->getMaxResults(); + } elseif ($queryBuilder->getMaxResults() > 0) { + $queryParts['LIMIT'] = $queryBuilder->getMaxResults(); + } + + return $queryParts; + } + + /** + * Helper to transform the QueryBuilder join part into a SQL fragment. + * + * @throws \RuntimeException + */ + protected function getQueryArrayJoinHelper(string $fromAlias, array $joinParts, array &$knownAliases): string + { + $sql = ''; + + if (isset($joinParts['join'][$fromAlias])) { + foreach ($joinParts['join'][$fromAlias] as $join) { + if (array_key_exists($join['joinAlias'], $knownAliases)) { + throw new \RuntimeException( + 'Non unique join alias: "' . $join['joinAlias'] . '" found.', + 1472748872 + ); + } + $sql .= ' ' . strtoupper($join['joinType']) + . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] + . ' ON ' . ((string)$join['joinCondition']); + $knownAliases[$join['joinAlias']] = true; + } + + foreach ($joinParts['join'][$fromAlias] as $join) { + $sql .= $this->getQueryArrayJoinHelper($join['joinAlias'], $joinParts, $knownAliases); + } + } + + return $sql; + } + + /** + * Builds list of marker values for handling PDO-like parameter markers in select parts. + * Marker values support stdWrap functionality thus allowing a way to use stdWrap functionality in various + * properties of 'select' AND prevents SQL-injection problems by quoting and escaping of numeric values, strings, + * NULL values and comma separated lists. + * + * @param string $table Table to select records from + * @param array $conf Select part of CONTENT definition + * @return array List of values to replace markers with + * @internal + * @see getQuery() + */ + public function getQueryMarkers(string $table, array $conf): array + { + if (!isset($conf['markers.']) || !is_array($conf['markers.'])) { + return []; + } + // Parse markers and prepare their values + $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $markerValues = []; + foreach ($conf['markers.'] as $dottedMarker => $dummy) { + $marker = rtrim($dottedMarker, '.'); + if ($dottedMarker != $marker . '.') { + continue; + } + // Parse definition + // todo else value is always null + $tempValue = isset($conf['markers.'][$dottedMarker]) + ? $this->cObj->stdWrap($conf['markers.'][$dottedMarker]['value'] ?? '', $conf['markers.'][$dottedMarker]) + : $conf['markers.'][$dottedMarker]['value']; + // Quote/escape if needed + if (is_numeric($tempValue)) { + if ((int)$tempValue == $tempValue) { + // Handle integer + $markerValues[$marker] = (int)$tempValue; + } else { + // Handle float + $markerValues[$marker] = (float)$tempValue; + } + } elseif ($tempValue === null) { + // It represents NULL + $markerValues[$marker] = 'NULL'; + } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) { + // See if it is really a comma separated list of values + $explodeValues = GeneralUtility::trimExplode(',', $tempValue); + if (count($explodeValues) > 1) { + // Handle each element of list separately + $tempArray = []; + foreach ($explodeValues as $listValue) { + if (is_numeric($listValue)) { + if ((int)$listValue == $listValue) { + $tempArray[] = (int)$listValue; + } else { + $tempArray[] = (float)$listValue; + } + } else { + // If quoted, remove quotes before + // escaping. + if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) { + $listValue = $matches[1]; + } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) { + $listValue = $matches[1]; + } + $tempArray[] = $connection->quote($listValue); + } + } + $markerValues[$marker] = implode(',', $tempArray); + } else { + // Handle remaining values as string + $markerValues[$marker] = $connection->quote($tempValue); + } + } else { + // Handle remaining values as string + $markerValues[$marker] = $connection->quote($tempValue); + } + } + return $markerValues; + } + + /** + * Helper function for getQuery(), creating the WHERE clause of the SELECT query + * + * @param string $table The table name + * @param array $conf The TypoScript configuration properties + * @return array Associative array containing the prepared data for WHERE, ORDER BY and GROUP BY fragments + * @throws \InvalidArgumentException + * @see getQuery() + */ + protected function getQueryConstraints(string $table, array $conf): array + { + // Init: + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $expressionBuilder = $queryBuilder->expr(); + $tsfe = $this->getTypoScriptFrontendController(); + $constraints = []; + $pid_uid_flag = 0; + $enableFieldsIgnore = []; + $queryParts = [ + 'where' => null, + 'groupBy' => null, + 'orderBy' => null, + ]; + + $isInWorkspace = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('workspace', 'isOffline'); + $considerMovePointers = ( + $isInWorkspace && $table !== 'pages' + && !empty($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) + ); + + if (trim($conf['uidInList'] ?? '')) { + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['uidInList'])); + + // If moved records shall be considered, select via t3ver_oid + if ($considerMovePointers) { + $constraints[] = (string)$expressionBuilder->orX( + $expressionBuilder->in($table . '.uid', $listArr), + $expressionBuilder->andX( + $expressionBuilder->eq( + $table . '.t3ver_state', + (int)(string)VersionState::cast(VersionState::MOVE_POINTER) + ), + $expressionBuilder->in($table . '.t3ver_oid', $listArr) + ) + ); + } else { + $constraints[] = (string)$expressionBuilder->in($table . '.uid', $listArr); + } + $pid_uid_flag++; + } + + // Static_* tables are allowed to be fetched from root page + if (strpos($table, 'static_') === 0) { + $pid_uid_flag++; + } + + if (trim($conf['pidInList'])) { + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['pidInList'])); + // Removes all pages which are not visible for the user! + $listArr = $this->checkPidArray($listArr); + if (GeneralUtility::inList($conf['pidInList'], 'root')) { + $listArr[] = 0; + } + if (GeneralUtility::inList($conf['pidInList'], '-1')) { + $listArr[] = -1; + $enableFieldsIgnore['pid'] = true; + } + if (!empty($listArr)) { + $constraints[] = $expressionBuilder->in($table . '.pid', array_map('intval', $listArr)); + $pid_uid_flag++; + } else { + // If not uid and not pid then uid is set to 0 - which results in nothing!! + $pid_uid_flag = 0; + } + } + + // If not uid and not pid then uid is set to 0 - which results in nothing!! + if (!$pid_uid_flag && trim($conf['pidInList'] ?? '') != 'ignore') { + $constraints[] = $expressionBuilder->eq($table . '.uid', 0); + } + + $where = trim((string)$this->cObj->stdWrapValue('where', $conf ?? [])); + if ($where) { + $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where); + } + + // Check if the default language should be fetched (= doing overlays), or if only the records of a language should be fetched + // but only do this for TCA tables that have languages enabled + $languageConstraint = $this->getLanguageRestriction($expressionBuilder, $table, $conf, GeneralUtility::makeInstance(Context::class)); + if ($languageConstraint !== null) { + $constraints[] = $languageConstraint; + } + + // Enablefields + if ($table === 'pages') { + $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_hid_del); + $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_groupAccess); + } else { + $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->enableFields($table, -1, $enableFieldsIgnore)); + } + + // MAKE WHERE: + if (count($constraints) !== 0) { + $queryParts['where'] = $expressionBuilder->andX(...$constraints); + } + // GROUP BY + $groupBy = trim((string)$this->cObj->stdWrapValue('groupBy', $conf ?? [])); + if ($groupBy) { + $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy); + } + + // ORDER BY + $orderByString = trim((string)$this->cObj->stdWrapValue('orderBy', $conf ?? [])); + if ($orderByString) { + $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString); + } + + // Return result: + return $queryParts; + } + + /** + * Removes Page UID numbers from the input array which are not available due to enableFields() or the list of bad doktype numbers ($this->checkPid_badDoktypeList) + * + * @param int[] $pageIds Array of Page UID numbers for select and for which pages with enablefields and bad doktypes should be removed. + * @return array Returns the array of remaining page UID numbers + * @internal + */ + public function checkPidArray(array $pageIds): array + { + if (empty($pageIds)) { + return []; + } + $restrictionContainer = GeneralUtility::makeInstance(FrontendRestrictionContainer::class); + $restrictionContainer->add(GeneralUtility::makeInstance( + DocumentTypeExclusionRestriction::class, + GeneralUtility::intExplode(',', (string)$this->cObj->checkPid_badDoktypeList, true) + )); + return $this->getTypoScriptFrontendController()->sys_page->filterAccessiblePageIds($pageIds, $restrictionContainer); + } + + /** + * Adds parts to the WHERE clause that are related to language. + * This only works on TCA tables which have the [ctrl][languageField] field set or if they + * have select.languageField = my_language_field set explicitly. + * + * It is also possible to disable the language restriction for a query by using select.languageField = 0, + * if select.languageField is not explicitly set, the TCA default values are taken. + * + * If the table is "localizeable" (= any of the criteria above is met), then the DB query is restricted: + * + * If the current language aspect has overlays enabled, then the only records with language "0" or "-1" are + * fetched (the overlays are taken care of later-on). + * if the current language has overlays but also records without localization-parent (free mode) available, + * then these are fetched as well. This can explicitly set via select.includeRecordsWithoutDefaultTranslation = 1 + * which overrules the overlayType within the language aspect. + * + * If the language aspect has NO overlays enabled, it behaves as in "free mode" (= only fetch the records + * for the current language. + * + * @param ExpressionBuilder $expressionBuilder + * @param string $table + * @param array $conf + * @param Context $context + * @return string|\TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression|null + * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException + */ + protected function getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context) + { + $languageField = ''; + $localizationParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? null; + // Check if the table is translatable, and set the language field by default from the TCA information + if (!empty($conf['languageField']) || !isset($conf['languageField'])) { + if (isset($conf['languageField']) && !empty($GLOBALS['TCA'][$table]['columns'][$conf['languageField']])) { + $languageField = $conf['languageField']; + } elseif (!empty($GLOBALS['TCA'][$table]['ctrl']['languageField']) && !empty($localizationParentField)) { + $languageField = $table . '.' . $GLOBALS['TCA'][$table]['ctrl']['languageField']; + } + } + + // No language restriction enabled explicitly or available via TCA + if (empty($languageField)) { + return null; + } + + /** @var LanguageAspect $languageAspect */ + $languageAspect = $context->getAspect('language'); + if ($languageAspect->doOverlays() && !empty($localizationParentField)) { + // Sys language content is set to zero/-1 - and it is expected that whatever routine processes the output will + // OVERLAY the records with localized versions! + $languageQuery = $expressionBuilder->in($languageField, [0, -1]); + // Use this option to include records that don't have a default language counterpart ("free mode") + // (originalpointerfield is 0 and the language field contains the requested language) + if (isset($conf['includeRecordsWithoutDefaultTranslation']) || !empty($conf['includeRecordsWithoutDefaultTranslation.'])) { + $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) + ? $this->cObj->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) + : $conf['includeRecordsWithoutDefaultTranslation']; + $includeRecordsWithoutDefaultTranslation = trim($includeRecordsWithoutDefaultTranslation) !== ''; + } else { + // Option was not explicitly set, check what's in for the language overlay type. + $includeRecordsWithoutDefaultTranslation = $languageAspect->getOverlayType() === $languageAspect::OVERLAYS_ON_WITH_FLOATING; + } + if ($includeRecordsWithoutDefaultTranslation) { + $languageQuery = $expressionBuilder->orX( + $languageQuery, + $expressionBuilder->andX( + $expressionBuilder->eq($table . '.' . $localizationParentField, 0), + $expressionBuilder->eq($languageField, $languageAspect->getContentId()) + ) + ); + } + return $languageQuery; + } + // No overlays = only fetch records given for the requested language and "all languages" + return $expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]); + } + + /** + * Helper function for getQuery, sanitizing the select part + * + * This functions checks if the necessary fields are part of the select + * and adds them if necessary. + * + * @param string $selectPart Select part + * @param string $table Table to select from + * @return string Sanitized select part + * @internal + * @see getQuery + */ + protected function sanitizeSelectPart(string $selectPart, string $table): string + { + $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + + // Pattern matching parts + $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)'; + $matchEnd = '(\\s*,|\\s*$)/'; + $necessaryFields = ['uid', 'pid']; + $wsFields = ['t3ver_state']; + if (isset($GLOBALS['TCA'][$table]) && !preg_match($matchStart . '\\*' . $matchEnd, $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)|distinct/i', $selectPart)) { + foreach ($necessaryFields as $field) { + $match = $matchStart . $field . $matchEnd; + if (!preg_match($match, $selectPart)) { + $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field); + } + } + if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ?? false) { + foreach ($wsFields as $field) { + $match = $matchStart . $field . $matchEnd; + if (!preg_match($match, $selectPart)) { + $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field); + } + } + } + } + return $selectPart; + } + + /** + * Performs basic mathematical evaluation of the input string. Does NOT take parenthesis and operator precedence into account! (for that, see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction()) + * + * @param string $val The string to evaluate. Example: "3+4*10/5" will generate "35". Only integer numbers can be used. + * @return int The result (might be a float if you did a division of the numbers). + * @see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction() + */ + public function calc($val) + { + $parts = GeneralUtility::splitCalc($val, '+-*/'); + $value = 0; + foreach ($parts as $part) { + $theVal = $part[1]; + $sign = $part[0]; + if ((string)(int)$theVal === (string)$theVal) { + $theVal = (int)$theVal; + } else { + $theVal = 0; + } + if ($sign === '-') { + $value -= $theVal; + } + if ($sign === '+') { + $value += $theVal; + } + if ($sign === '/') { + if ((int)$theVal) { + $value /= (int)$theVal; + } + } + if ($sign === '*') { + $value *= $theVal; + } + } + return $value; + } + + /** + * @return TimeTracker + */ + protected function getTimeTracker() + { + return GeneralUtility::makeInstance(TimeTracker::class); + } + + /** + * Returns the current BE user. + * + * @return FrontendBackendUserAuthentication + */ + protected function getFrontendBackendUser() + { + return $GLOBALS['BE_USER']; + } + + /** + * @return TypoScriptFrontendController|null + */ + protected function getTypoScriptFrontendController() + { + return $GLOBALS['TSFE'] ?? null; + } +} diff --git a/Classes/EventListener/CssMerger.php b/Classes/EventListener/CssMerger.php old mode 100755 new mode 100644 index b17ae2a..dfd8de0 --- a/Classes/EventListener/CssMerger.php +++ b/Classes/EventListener/CssMerger.php @@ -1,19 +1,7 @@ isInline() && $event->isPriority()) { @@ -45,7 +34,6 @@ class CssMerger $assetCollector->addInlineStyleSheet('ew_base', $styles, [], ['priority' => true]); } else { $temporaryFile = GeneralUtility::writeStyleSheetContentToTemporaryFile($styles); - // @extensionScannerIgnoreLine $assetCollector->addStyleSheet('combined_styles', $temporaryFile); } } diff --git a/Classes/EventListener/IsContentUsedOnPageLayout.php b/Classes/EventListener/IsContentUsedOnPageLayout.php new file mode 100644 index 0000000..daafc1e --- /dev/null +++ b/Classes/EventListener/IsContentUsedOnPageLayout.php @@ -0,0 +1,25 @@ +setUsed($event->isRecordUsed() || $this->findCTypeBegin($event->getRecord()['CType'])); + } + + public function findCTypeBegin($cType): bool + { + $found = false; + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { + if (strpos($cType, $search) !== false) { + $found = true; + break; + } + } + return $found; + } +} diff --git a/Classes/EventListener/JsMerger.php b/Classes/EventListener/JsMerger.php old mode 100755 new mode 100644 index 3b4b873..52d7381 --- a/Classes/EventListener/JsMerger.php +++ b/Classes/EventListener/JsMerger.php @@ -1,19 +1,7 @@ isInline() && $event->isPriority()) { diff --git a/Classes/Form/Element/PickColorFromImage.php b/Classes/Form/Element/PickColorFromImage.php old mode 100755 new mode 100644 index bc3c9c9..705918b --- a/Classes/Form/Element/PickColorFromImage.php +++ b/Classes/Form/Element/PickColorFromImage.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace Evoweb\EwBase\Form\Element; + /* * This file is developed by evoWeb. * @@ -13,14 +15,8 @@ declare(strict_types=1); * LICENSE.txt file that was distributed with this source code. */ -namespace Evoweb\EwBase\Form\Element; - -use Psr\EventDispatcher\EventDispatcherInterface; use TYPO3\CMS\Backend\Form\Element\AbstractFormElement; -use TYPO3\CMS\Backend\Form\Event\ModifyImageManipulationPreviewUrlEvent; -use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\View\BackendViewFactory; -use TYPO3\CMS\Core\Crypto\HashService; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException; @@ -34,8 +30,6 @@ use TYPO3\CMS\Core\Utility\StringUtility; class PickColorFromImage extends AbstractFormElement { - private string $wizardRouteName = 'ajax_wizard_image_manipulation'; - /** * Default element configuration */ @@ -114,11 +108,9 @@ class PickColorFromImage extends AbstractFormElement public function __construct( private readonly BackendViewFactory $backendViewFactory, - private readonly UriBuilder $uriBuilder, - private readonly EventDispatcherInterface $eventDispatcher, private readonly ResourceFactory $resourceFactory, - private readonly HashService $hashService, - ) {} + ) { + } public function render(): array { @@ -146,6 +138,14 @@ class PickColorFromImage extends AbstractFormElement $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); + $width = $this->formMaxWidth( + MathUtility::forceIntegerInRange( + $config['size'] ?? $this->defaultInputWidth, + $this->minimumInputWidth, + $this->maxInputWidth + ) + ); + $arguments = [ 'fieldInformation' => $fieldInformationHtml, 'fieldControl' => $fieldControlHtml, @@ -164,15 +164,12 @@ class PickColorFromImage extends AbstractFormElement 'validation' => '[]', ], 'config' => $config, - 'wizardUri' => $this->getWizardUri(), - 'wizardPayload' => json_encode($this->getWizardPayload($config['cropVariants'], $file)), - 'previewUrl' => $this->eventDispatcher->dispatch( - new ModifyImageManipulationPreviewUrlEvent($this->data['databaseRow'], $config, $file) - )->getPreviewUrl(), + 'width' => $width, ]; if ($arguments['isAllowedFileExtension']) { $fieldId = StringUtility::getUniqueId('formengine-color-'); + $resultArray['stylesheetFiles'][] = 'EXT:ew_base/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css'; $resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create( @@ -189,6 +186,9 @@ class PickColorFromImage extends AbstractFormElement } } $view = $this->backendViewFactory->create($this->data['request']); + $templatePaths = $view->getRenderingContext()->getTemplatePaths(); + $templatePaths->setTemplateRootPaths(['EXT:ew_base/Resources/Private/Templates']); + $templatePaths->setPartialRootPaths(['EXT:ew_base/Resources/Private/Partials']); $view->assignMultiple($arguments); $resultArray['html'] = $view->render('Form/ImageManipulationElement'); @@ -211,19 +211,12 @@ class PickColorFromImage extends AbstractFormElement return $file; } - /** - * @throws InvalidConfigurationException - */ protected function populateConfiguration(array $baseConfiguration): array { $defaultConfig = self::$defaultConfig; - - // If ratios are set do not add default options - if (isset($baseConfiguration['cropVariants'])) { - unset($defaultConfig['cropVariants']); - } - $config = array_replace_recursive($defaultConfig, $baseConfiguration); + $imageConfig = $this->data['processedTca']['columns'][$config['imageField']]; + $config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants']; if (!is_array($config['cropVariants'])) { throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267); @@ -235,7 +228,7 @@ class PickColorFromImage extends AbstractFormElement $cropVariant['allowedAspectRatios'] = array_filter( $cropVariant['allowedAspectRatios'] ?? [], static function ($aspectRatio) { - return !($aspectRatio['disabled'] ?? false); + return !(bool)($aspectRatio['disabled'] ?? false); } ); @@ -261,8 +254,8 @@ class PickColorFromImage extends AbstractFormElement $config['cropVariants'] = $cropVariants; - // By default, we allow all image extensions that can be handled by the GFX functionality $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; + return $config; } @@ -280,22 +273,4 @@ class PickColorFromImage extends AbstractFormElement ); return $config; } - - protected function getWizardUri(): string - { - return (string)$this->uriBuilder->buildUriFromRoute($this->wizardRouteName); - } - - protected function getWizardPayload(array $cropVariants, File $image): array - { - $uriArguments = []; - $arguments = [ - 'cropVariants' => $cropVariants, - 'image' => $image->getUid(), - ]; - $uriArguments['arguments'] = json_encode($arguments); - $uriArguments['signature'] = $this->hashService->hmac((string)($uriArguments['arguments']), $this->wizardRouteName); - - return $uriArguments; - } } diff --git a/Classes/Hooks/PageLayoutView.php b/Classes/Hooks/PageLayoutView.php new file mode 100644 index 0000000..608ccf1 --- /dev/null +++ b/Classes/Hooks/PageLayoutView.php @@ -0,0 +1,34 @@ +findCTypeBegin($params['record']['CType']); + } + + public function findCTypeBegin($cType): bool + { + $found = false; + foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { + if (str_contains($cType, $search)) { + $found = true; + break; + } + } + return $found; + } +} diff --git a/Classes/ToolbarItems/ReleaseToolbarItem.php b/Classes/ToolbarItems/ReleaseToolbarItem.php old mode 100755 new mode 100644 index 097ba9a..4f01aa4 --- a/Classes/ToolbarItems/ReleaseToolbarItem.php +++ b/Classes/ToolbarItems/ReleaseToolbarItem.php @@ -1,5 +1,7 @@ logger = $logManager->getLogger(self::class); + } + + public function getTitle(): string + { + return 'Migrate gridelements to container'; + } + + public function getDescription(): string + { + return 'Migrates content elements that are type gridelements to corresponding container elements'; + } + + public function getPrerequisites(): array + { + return [ + DatabaseUpdatedPrerequisite::class + ]; + } + + public function updateNecessary(): bool + { + return $this->hasRecordsToUpdate(); + } + + public function executeUpdate(): bool + { + $updateSuccessful = true; + try { + $this->service->migrate(); + } catch (\Exception $exception) { + $this->logger->log(LogLevel::ERROR, $exception->getMessage()); + $updateSuccessful = false; + } + + return $updateSuccessful; + } + + protected function hasRecordsToUpdate(): bool + { + return (bool)$this->getPreparedQueryBuilder()->count('uid')->executeQuery()->fetchOne(); + } + + protected function getPreparedQueryBuilder(): QueryBuilder + { + $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); + $queryBuilder->getRestrictions() + ->removeByType(HiddenRestriction::class) + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class); + $queryBuilder + ->from(self::TABLE_NAME) + ->where( + $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')) + ); + return $queryBuilder; + } + + protected function getConnectionPool(): ConnectionPool + { + return GeneralUtility::makeInstance(ConnectionPool::class); + } +} diff --git a/Classes/Updates/GridelementsToContainerService.php b/Classes/Updates/GridelementsToContainerService.php new file mode 100644 index 0000000..4b0cdac --- /dev/null +++ b/Classes/Updates/GridelementsToContainerService.php @@ -0,0 +1,268 @@ + [ + 'CType' => 'three-col-columns', + ], + '140' => [ + 'CType' => [ + 'search' => 'pi_flexform/type', + 'matches' => [ + 0 => 'two-col-columns-11', + 1 => 'two-col-columns-12', + 2 => 'two-col-columns-21', + ], + ], + 'map' => [ + 'pi_flexform/row_class' => 'frame_class', + ] + ], +]; +*/ + +class GridelementsToContainerService +{ + private const TABLE_NAME = 'tt_content'; + + protected array $resolveContainer = []; + + protected int $colPosOffset = 0; + + protected array $configuration = []; + + public function __construct( + protected ConnectionPool $connectionPool, + protected DataHandler $dataHandler, + protected FlexFormService $flexFormService, + ) { + $config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; + $this->resolveContainer = $config['migrationResolveContainer'] ?? []; + $this->colPosOffset = $config['migrationColPosOffset'] ?? 0; + $this->configuration = $config['migrationMapping'] ?? []; + } + + public function migrate(): void + { + $this->initializeDataHandler(); + + // Move children out of container and remove container + foreach ($this->resolveContainer as $containerId) { + $this->resolveContainers((string)$containerId); + } + + $this->migrateConfiguredContainer(); + } + + protected function initializeDataHandler(): void + { + $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); + $backendUser->user = [ + 'uid' => 0, + 'admin' => 1, + ]; + $this->dataHandler->start([], [], $backendUser); + } + + + protected function resolveContainers(string $layout): void + { + $containers = $this->getGridElementsByLayout($layout); + foreach ($containers as $container) { + $this->processContainerResolve($container); + } + } + + protected function processContainerResolve(array $container): void + { + $children = $this->getGridContainerChildren($container['uid']); + // move first child after container + $moveAfterThis = $container; + foreach ($children as $child) { + [$moveAfterThis, $container] = $this->processContainerResolveChild($child, $moveAfterThis, $container); + } + + $this->deleteElement($container['uid']); + } + + protected function processContainerResolveChild(array $child, array $moveAfterThis, array $container): array + { + $this->updateElement( + $child['uid'], + [ + 'tx_gridelements_container' => 0, + 'colPos' => $container['colPos'], + 'header' => $child['header'] ?: $container['header'] + ] + ); + $this->moveElementAfterElement($child['uid'], $moveAfterThis['uid']); + // use this child to move the next child after + $moveAfterThis = $child; + // empty container header so only the first child gets the header + $container['header'] = ''; + + return [$moveAfterThis, $container]; + } + + protected function deleteElement(int $uid): void + { + $this->connectionPool + ->getConnectionForTable(self::TABLE_NAME) + ->update(self::TABLE_NAME, ['delete' => 1], ['uid' => $uid]); + } + + protected function moveElementAfterElement(int $elementToMove, int $elementToMoveAfter): void + { + $this->dataHandler->moveRecord(self::TABLE_NAME, $elementToMove, $elementToMoveAfter * -1); + } + + + protected function migrateConfiguredContainer(): void + { + array_walk($this->configuration, function($config, $key) { + $containers = $this->getGridElementsByLayout((string)$key); + foreach ($containers as $container) { + $container['pi_flexform'] = $this->flexFormService->convertFlexFormContentToArray( + $container['pi_flexform'] + ); + $this->processContainerMigration($container, $config); + } + }); + } + + protected function processContainerMigration(array $container, array $config): void + { + $children = $this->getGridContainerChildren($container['uid']); + foreach ($children as $child) { + $this->processContainerMigrationChild($child, $container); + } + + $data = [ + 'CType' => $this->getCType($container, $config), + 'tx_gridelements_backend_layout' => '' + ]; + + if (isset($config['map']) && is_array($config['map']) && !empty($container['pi_flexform'])) { + $data = $this->addMappedValues($data, $container, $config['map']); + } + + $this->updateElement($container['uid'], $data); + } + + protected function processContainerMigrationChild(array $child, array $container): void + { + $this->updateElement( + $child['uid'], + [ + 'hidden' => $child['hidden'] ?: $container['hidden'], + 'colPos' => $child['tx_gridelements_columns'] + $this->colPosOffset, + 'tx_container_parent' => $child['tx_gridelements_container'], + 'tx_gridelements_columns' => 0, + 'tx_gridelements_container' => 0, + ] + ); + } + + protected function getCType(array $container, array $config): string + { + if (is_array($config['CType'])) { + $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); + $result = $config['CType']['matches'][$value] ?? null; + } else { + $result = $config['CType'] ?? null; + } + + if (empty($result)) { + throw new \Exception('CType must always be set'); + } + + return $result; + } + + protected function addMappedValues(array $data, array $container, array $config): array + { + foreach ($config as $from => $to) { + try { + $value = ArrayUtility::getValueByPath($container, $from); + if (empty($container[$to]) && !empty($value)) { + $data[$to] = $value; + } + } catch (\throwable) {} + } + + return $data; + } + + + protected function updateElement(int $uid, array $changes): void + { + $this->connectionPool + ->getConnectionForTable(self::TABLE_NAME) + ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); + } + + protected function getGridContainerChildren(int $containerUid): array + { + $queryBuilder = $this->getQueryBuilderForTable(); + + return $queryBuilder + ->select('*') + ->where( + $queryBuilder->expr()->eq( + 'tx_gridelements_container', + $queryBuilder->createNamedParameter($containerUid, \PDO::PARAM_INT) + ) + ) + ->orderBy('sorting') + ->executeQuery() + ->fetchAllAssociative(); + } + + protected function getGridElementsByLayout(string $layout): array + { + $queryBuilder = $this->getQueryBuilderForTable(); + $expr = $queryBuilder->expr(); + + return $queryBuilder + ->select('*') + ->where( + $expr->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')), + $expr->eq('tx_gridelements_backend_layout', $queryBuilder->createNamedParameter($layout)) + ) + ->orderBy('sorting') + ->executeQuery() + ->fetchAllAssociative(); + } + + protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($table); + + $queryBuilder->getRestrictions() + ->removeByType(HiddenRestriction::class) + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class); + + $queryBuilder->from($table); + + return $queryBuilder; + } +} diff --git a/Classes/Updates/ParentChildToContainerMigration.php b/Classes/Updates/ParentChildToContainerMigration.php new file mode 100644 index 0000000..685daab --- /dev/null +++ b/Classes/Updates/ParentChildToContainerMigration.php @@ -0,0 +1,106 @@ +logger = $logManager->getLogger(self::class); + } + + public function getTitle(): string + { + return 'Migrate parent/child to container'; + } + + public function getDescription(): string + { + return 'Migrates content elements that are type parent/child to corresponding container elements'; + } + + public function getPrerequisites(): array + { + return [ + DatabaseUpdatedPrerequisite::class + ]; + } + + public function updateNecessary(): bool + { + return $this->hasRecordsToUpdate(); + } + + public function executeUpdate(): bool + { + $updateSuccessful = true; + try { + $this->service->migrate(); + } catch (\Exception $exception) { + $this->logger->log(LogLevel::ERROR, $exception->getMessage()); + $updateSuccessful = false; + } + + return $updateSuccessful; + } + + protected function hasRecordsToUpdate(): bool + { + $queryBuilder = $this->getPreparedQueryBuilder(); + $tableColumns = $queryBuilder + ->getConnection() + ->createSchemaManager() + ->listTableColumns(self::TABLE_NAME); + return isset($tableColumns['parent']) + && $queryBuilder + ->count('uid') + ->executeQuery() + ->fetchOne() > 0; + } + + protected function getPreparedQueryBuilder(): QueryBuilder + { + $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); + $queryBuilder->getRestrictions() + ->removeByType(HiddenRestriction::class) + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class); + $queryBuilder + ->from(self::TABLE_NAME) + ->where( + // check if there are still records that have parent instead of tx_container_parent + $queryBuilder->expr()->gt('parent', 0) + ); + return $queryBuilder; + } + + protected function getConnectionPool(): ConnectionPool + { + return GeneralUtility::makeInstance(ConnectionPool::class); + } +} diff --git a/Classes/Updates/ParentChildToContainerService.php b/Classes/Updates/ParentChildToContainerService.php new file mode 100644 index 0000000..737ed75 --- /dev/null +++ b/Classes/Updates/ParentChildToContainerService.php @@ -0,0 +1,168 @@ + [ 'CType' => 'container-downloads' ], +]; +*/ + +class ParentChildToContainerService +{ + private const TABLE_NAME = 'tt_content'; + + protected int $colPosOffset = 0; + + protected array $configuration = []; + + public function __construct( + protected ConnectionPool $connectionPool, + protected DataHandler $dataHandler, + protected FlexFormService $flexFormService, + ) { + $config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; + $this->colPosOffset = $config['childParentColPosOffset'] ?? 0; + $this->configuration = $config['childParentMigrationMapping'] ?? []; + } + + public function migrate(): void + { + $this->initializeDataHandler(); + $this->migrateConfiguredContainer(); + } + + protected function initializeDataHandler(): void + { + $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); + $backendUser->user = [ + 'uid' => 0, + 'admin' => 1, + ]; + $this->dataHandler->start([], [], $backendUser); + } + + + protected function migrateConfiguredContainer(): void + { + array_walk($this->configuration, function($config, $key) { + $parents = $this->getParentsByCType((string)$key); + foreach ($parents as $parent) { + $this->processParentMigration($parent, $config); + } + }); + } + + protected function processParentMigration(array $parent, array $config): void + { + $children = $this->getChildren($parent['uid']); + foreach ($children as $child) { + $this->processChildMigration($child, $parent); + } + + $this->updateElement( + $parent['uid'], + [ + 'CType' => $this->getCType($parent, $config), + 'children' => 0, + ] + ); + } + + protected function processChildMigration(array $child, array $parent): void + { + $this->updateElement( + $child['uid'], + [ + 'tx_container_parent' => $parent['uid'], + 'colPos' => $child['colPos'] + $this->colPosOffset, + 'parent' => 0, + ] + ); + } + + protected function getCType(array $container, array $config): string + { + if (is_array($config['CType'])) { + $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); + $result = $config['CType']['matches'][$value] ?? null; + } else { + $result = $config['CType'] ?? null; + } + + if (empty($result)) { + throw new \Exception('CType must always be set'); + } + + return $result; + } + + + protected function updateElement(int $uid, array $changes): void + { + $this->connectionPool + ->getConnectionForTable(self::TABLE_NAME) + ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); + } + + protected function getChildren(int $parentUid): array + { + $queryBuilder = $this->getQueryBuilderForTable(); + + return $queryBuilder + ->select('*') + ->where( + $queryBuilder->expr()->eq( + 'parent', + $queryBuilder->createNamedParameter($parentUid, \PDO::PARAM_INT) + ) + ) + ->orderBy('sorting') + ->executeQuery() + ->fetchAllAssociative(); + } + + protected function getParentsByCType(string $cType): array + { + $queryBuilder = $this->getQueryBuilderForTable(); + $expr = $queryBuilder->expr(); + + return $queryBuilder + ->select('*') + ->where( + $expr->eq('CType', $queryBuilder->createNamedParameter($cType)) + ) + ->orderBy('sorting') + ->executeQuery() + ->fetchAllAssociative(); + } + + protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable($table); + + $queryBuilder->getRestrictions() + ->removeByType(HiddenRestriction::class) + ->removeByType(StartTimeRestriction::class) + ->removeByType(EndTimeRestriction::class); + + $queryBuilder->from($table); + + return $queryBuilder; + } +} diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php old mode 100755 new mode 100644 index 58c18b2..9659830 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -2,19 +2,9 @@ declare(strict_types=1); -/* - * This file is developed by evoWeb. - * - * 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. - */ - namespace Evoweb\EwBase\User; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; /** @@ -28,6 +18,8 @@ class AssetPath { public function getAbsolutePublicPath(string $content, array $conf): string { - return $content . PathUtility::getAbsoluteWebPath($conf['file']); + return $content + . GeneralUtility::getIndpEnv('TYPO3_SITE_URL') + . ltrim(PathUtility::getPublicResourceWebPath($conf['file']), '/'); } } diff --git a/Classes/ViewHelpers/Array/AddViewHelper.php b/Classes/ViewHelpers/Array/AddViewHelper.php old mode 100755 new mode 100644 index fed0fcb..1d0b5c9 --- a/Classes/ViewHelpers/Array/AddViewHelper.php +++ b/Classes/ViewHelpers/Array/AddViewHelper.php @@ -1,5 +1,7 @@ registerArgument('array', 'array', 'Array to add value to'); @@ -34,11 +34,18 @@ class AddViewHelper extends AbstractViewHelper $this->registerArgument('value', 'mixed', 'Value to add'); } + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return array + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): array { + ) { $array = $arguments['array'] ?: []; $key = $arguments['key']; $value = !is_null($arguments['value']) ? $arguments['value'] : $renderChildrenClosure(); diff --git a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php old mode 100755 new mode 100644 index f9bf1fd..8449574 --- a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php +++ b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php @@ -15,7 +15,6 @@ declare(strict_types=1); namespace Evoweb\EwBase\ViewHelpers\Be; -use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; @@ -30,7 +29,10 @@ class ThumbnailViewHelper extends AbstractViewHelper { use CompileWithRenderStatic; - public function initializeArguments(): void + /** + * Initializes the arguments + */ + public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('row', 'array', 'content data', true); @@ -38,11 +40,20 @@ class ThumbnailViewHelper extends AbstractViewHelper $this->registerArgument('fieldName', 'string', 'field name', true); } + /** + * Render a constant + * + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return string Value of constant + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): string { + ) { $row = $arguments['row']; $tableName = $arguments['tableName']; $fieldName = $arguments['fieldName']; @@ -75,8 +86,7 @@ class ThumbnailViewHelper extends AbstractViewHelper $row['uid'] => 'edit', ], ], - // @extensionScannerIgnoreLine - 'returnUrl' => self::getRequest()->getAttribute('normalizedParams')->getRequestUri() + 'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri() . '#element-tt_content-' . $row['uid'], ]; /** @var UriBuilder $uriBuilder */ @@ -100,9 +110,4 @@ class ThumbnailViewHelper extends AbstractViewHelper { return $GLOBALS['LANG']; } - - protected static function getRequest(): ServerRequestInterface - { - return $GLOBALS['TYPO3_REQUEST']; - } } diff --git a/Classes/ViewHelpers/Condition/InArrayViewHelper.php b/Classes/ViewHelpers/Condition/InArrayViewHelper.php old mode 100755 new mode 100644 index 52390db..bc713f5 --- a/Classes/ViewHelpers/Condition/InArrayViewHelper.php +++ b/Classes/ViewHelpers/Condition/InArrayViewHelper.php @@ -1,18 +1,14 @@ registerArgument('haystack', 'array', 'haystack', true); $this->registerArgument('needle', 'mixed', 'needle', true); } - protected static function evaluateCondition($arguments = null): bool + /** + * @param array $arguments + * @return bool + */ + protected static function evaluateCondition($arguments = null) { $array = $arguments['haystack']->toArray(); return in_array($arguments['needle'], $array); diff --git a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php old mode 100755 new mode 100644 index 1e6d3b8..efb6b6f --- a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php +++ b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php @@ -1,18 +1,14 @@ registerArgument('haystack', 'string', 'haystack', true); $this->registerArgument('needle', 'string', 'need', true); } - protected static function evaluateCondition($arguments = null): bool + /** + * @param array $arguments + * @return bool + */ + protected static function evaluateCondition($arguments = null) { - return str_contains($arguments['haystack'], $arguments['needle']); + return false !== strpos($arguments['haystack'], $arguments['needle']); } } diff --git a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php old mode 100755 new mode 100644 index 1d40d86..0875df1 --- a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php +++ b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php @@ -1,24 +1,24 @@ isDevelopment(); } diff --git a/Classes/ViewHelpers/Context/ProductionViewHelper.php b/Classes/ViewHelpers/Context/ProductionViewHelper.php old mode 100755 new mode 100644 index 957ac2d..f5356bb --- a/Classes/ViewHelpers/Context/ProductionViewHelper.php +++ b/Classes/ViewHelpers/Context/ProductionViewHelper.php @@ -1,24 +1,24 @@ isProduction(); } diff --git a/Classes/ViewHelpers/Context/StagingViewHelper.php b/Classes/ViewHelpers/Context/StagingViewHelper.php old mode 100755 new mode 100644 index e3e6e29..a76003e --- a/Classes/ViewHelpers/Context/StagingViewHelper.php +++ b/Classes/ViewHelpers/Context/StagingViewHelper.php @@ -1,25 +1,25 @@ + * .... + * + * + * fixed flexform data for extbase controller + * + * + * @api + */ +class FixFlexformForExtbaseViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * @var boolean + */ + protected $escapeOutput = false; + + public function initializeArguments(): void + { + parent::initializeArguments(); + $this->registerArgument('data', 'array', 'The data array of content element', true); + } + + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return array + */ + public static function renderStatic( + array $arguments, + \Closure $renderChildrenClosure, + RenderingContextInterface $renderingContext + ) { + $templateVariableContainer = $renderingContext->getVariableProvider(); + + /** @var FlexFormTools $flexFormTools */ + $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); + + $data = $arguments['data']; + if (is_array($data['pi_flexform'])) { + $data['pi_flexform'] = $flexFormTools->flexArray2Xml($data['pi_flexform']); + } + + $templateVariableContainer->add('data', $data); + $output = $renderChildrenClosure(); + $templateVariableContainer->remove('data'); + + return $output; + } +} diff --git a/Classes/ViewHelpers/FlexFormViewHelper.php b/Classes/ViewHelpers/FlexFormViewHelper.php new file mode 100644 index 0000000..cd7fc27 --- /dev/null +++ b/Classes/ViewHelpers/FlexFormViewHelper.php @@ -0,0 +1,80 @@ + + * .... + * + * + * fixed flexform data for extbase controller + * + * + * @api + */ +class FlexFormViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * @var boolean + */ + protected $escapeOutput = false; + + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('data', 'array', 'Array to get flex form data from', true); + $this->registerArgument('fieldName', 'string', 'Field name', false, 'pi_flexform'); + $this->registerArgument('as', 'string', 'Name of the variable to assign', false, 'flexFormData'); + } + + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return array + */ + public static function renderStatic( + array $arguments, + \Closure $renderChildrenClosure, + RenderingContextInterface $renderingContext + ) { + $data = []; + + if (is_array($arguments['data'])) { + if (isset($arguments['data'][$arguments['fieldName']])) { + $data = is_array($arguments['data'][$arguments['fieldName']]) ? + $arguments['data'][$arguments['fieldName']] : + GeneralUtility::xml2array($arguments['data'][$arguments['fieldName']]); + $data = $data['data'] ?? $data; + } + } + + $templateVariableContainer = $renderingContext->getVariableProvider(); + $templateVariableContainer->add($arguments['as'], $data); + $content = $renderChildrenClosure(); + $templateVariableContainer->remove($arguments['as']); + + return $content; + } +} diff --git a/Classes/ViewHelpers/HashViewHelper.php b/Classes/ViewHelpers/HashViewHelper.php old mode 100755 new mode 100644 index ac71cac..0b61138 --- a/Classes/ViewHelpers/HashViewHelper.php +++ b/Classes/ViewHelpers/HashViewHelper.php @@ -1,5 +1,7 @@ registerArgument('action', 'string', 'Target action'); $this->registerArgument('arguments', 'array', 'Arguments for the controller action, associative array'); } + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): string { + ) { $result = ''; if ( $arguments['action'] !== null && $arguments['arguments'] !== null && isset($arguments['arguments']['user']) ) { - /** @var HashService $hashService */ - $hashService = GeneralUtility::makeInstance(HashService::class); - $result = $hashService->hmac($arguments['action'] . '::' . $arguments['arguments']['user'], ''); + $result = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac( + $arguments['action'] . '::' . $arguments['arguments']['user'] + ); } return $result; diff --git a/Classes/ViewHelpers/Iterator/AddViewHelper.php b/Classes/ViewHelpers/Iterator/AddViewHelper.php new file mode 100644 index 0000000..bb24422 --- /dev/null +++ b/Classes/ViewHelpers/Iterator/AddViewHelper.php @@ -0,0 +1,56 @@ +registerArgument('array', 'array', 'Array to add value to'); + $this->registerArgument('key', 'string', 'Key to add value by', true); + $this->registerArgument('value', 'mixed', 'Value to add'); + } + + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return array + */ + public static function renderStatic( + array $arguments, + \Closure $renderChildrenClosure, + RenderingContextInterface $renderingContext + ) { + $array = $arguments['array'] ?: []; + $key = $arguments['key']; + $value = $arguments['value'] ?: $renderChildrenClosure(); + + return array_merge($array, [$key => $value]); + } +} diff --git a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php b/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php new file mode 100644 index 0000000..e50139f --- /dev/null +++ b/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php @@ -0,0 +1,93 @@ + ewb:iterator.explode(glue: 'constant:LF')} + * + * + * {as} + * + */ +class ExplodeViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + protected static string $method = 'explode'; + + public function initializeArguments() + { + $this->registerArgument( + 'as', + 'string', + 'Template variable name to assign; if not specified the ViewHelper returns the variable instead.' + ); + $this->registerArgument('content', 'string', 'String to be exploded by glue'); + $this->registerArgument( + 'glue', + 'string', + 'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" ' . + '(fx "constant:LF" for linefeed as glue)', + false, + ',' + ); + } + + /** + * Render method + * + * @return string|array + */ + public static function renderStatic( + array $arguments, + \Closure $renderChildrenClosure, + RenderingContextInterface $renderingContext + ) { + $content = $arguments['content'] ?? $renderChildrenClosure(); + $glue = static::resolveGlue($arguments); + $content = call_user_func_array(static::$method, [$glue, $content]); + + $as = $arguments['as']; + if (true === empty($as)) { + $output = $content; + } else { + $templateVariableContainer = $renderingContext->getVariableProvider(); + $templateVariableContainer->add($as, $content); + $output = $renderChildrenClosure(); + $templateVariableContainer->remove($as); + } + return $output; + } + + protected static function resolveGlue(array $arguments): string + { + $glue = $arguments['glue']; + if (false !== strpos($glue, ':') && 1 < strlen($glue)) { + // glue contains a special type identifier, resolve the actual glue + list ($type, $value) = explode(':', $glue); + switch ($type) { + case 'constant': + $glue = constant($value); + break; + default: + $glue = $value; + } + } + return $glue; + } +} diff --git a/Classes/ViewHelpers/PublicPathViewHelper.php b/Classes/ViewHelpers/PublicPathViewHelper.php old mode 100755 new mode 100644 index 899b19d..769f001 --- a/Classes/ViewHelpers/PublicPathViewHelper.php +++ b/Classes/ViewHelpers/PublicPathViewHelper.php @@ -2,6 +2,8 @@ declare(strict_types=1); +namespace Evoweb\EwBase\ViewHelpers; + /* * This file is developed by evoWeb. * @@ -13,8 +15,6 @@ declare(strict_types=1); * LICENSE.txt file that was distributed with this source code. */ -namespace Evoweb\EwBase\ViewHelpers; - use TYPO3\CMS\Core\Utility\PathUtility; use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; @@ -37,27 +37,31 @@ class PublicPathViewHelper extends AbstractViewHelper use CompileWithRenderStatic; /** - * @var bool + * @var boolean */ protected $escapeOutput = false; - public function initializeArguments(): void + protected static ?array $frontendGroupIds = null; + + public function initializeArguments() { parent::initializeArguments(); $this->registerArgument('path', 'string', 'Extension resource path', true); } + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return bool + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): string { + ) { $path = (string)$arguments['path']; - try { - $path = PathUtility::getPublicResourceWebPath($path); - } catch (\Exception) { - $path = ''; - } - return $path; + return PathUtility::getPublicResourceWebPath($path); } } diff --git a/Classes/ViewHelpers/ReplaceViewHelper.php b/Classes/ViewHelpers/ReplaceViewHelper.php old mode 100755 new mode 100644 index f05612d..77a9837 --- a/Classes/ViewHelpers/ReplaceViewHelper.php +++ b/Classes/ViewHelpers/ReplaceViewHelper.php @@ -1,5 +1,7 @@ registerArgument('value', 'string', 'String to replace in'); $this->registerArgument('search', 'string', 'Search string'); $this->registerArgument('replace', 'string', 'Replace value'); } + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return null + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): string { + ) { $content = $arguments['value']; if ($content === null) { $content = $renderChildrenClosure(); diff --git a/Classes/ViewHelpers/SvgViewHelper.php b/Classes/ViewHelpers/SvgViewHelper.php old mode 100755 new mode 100644 index e06b1df..2290936 --- a/Classes/ViewHelpers/SvgViewHelper.php +++ b/Classes/ViewHelpers/SvgViewHelper.php @@ -1,5 +1,7 @@ * */ -class SvgViewHelper extends AbstractViewHelper +class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper { use CompileWithRenderStatic; @@ -76,53 +74,30 @@ class SvgViewHelper extends AbstractViewHelper public function initializeArguments(): void { - $this->registerArgument( - 'identifier', - 'string', - 'Identifier of the icon as registered in the Icon Registry.', - true - ); - $this->registerArgument( - 'size', - 'string', - 'Desired size of the icon. All values of the Icons.sizes enum are allowed,' - . ' these are: "small", "default", "large" and "overlay".', - false, - IconSize::SMALL - ); - $this->registerArgument( - 'overlay', - 'string', - 'Identifier of an overlay icon as registered in the Icon Registry.', - false, - '' - ); - $this->registerArgument( - 'state', - 'string', - 'Sets the state of the icon. All values of the Icons.states enum are allowed,' - . ' these are: "default" and "disabled".', - false, - IconState::STATE_DEFAULT - ); - $this->registerArgument( - 'alternativeMarkupIdentifier', - 'string', - 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', - false, - '' - ); + $this->registerArgument('identifier', 'string', 'Identifier of the icon as registered in the Icon Registry.', true); + $this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, Icon::SIZE_SMALL); + $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false); + $this->registerArgument('state', 'string', 'Sets the state of the icon. All values of the Icons.states enum are allowed, these are: "default" and "disabled".', false, IconState::STATE_DEFAULT); + $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false); } + /** + * Prints icon html for $identifier key + * + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): Icon { + ) { $identifier = $arguments['identifier']; $size = $arguments['size']; $overlay = $arguments['overlay']; - $state = IconState::tryFrom($arguments['state']); + $state = IconState::cast($arguments['state']); $alternativeMarkupIdentifier = $arguments['alternativeMarkupIdentifier']; $iconFactory = GeneralUtility::makeInstance(IconFactory::class); return $iconFactory->getIcon($identifier, $size, $overlay, $state)->getMarkup($alternativeMarkupIdentifier); diff --git a/Classes/ViewHelpers/TrimViewHelper.php b/Classes/ViewHelpers/TrimViewHelper.php old mode 100755 new mode 100644 index 1d9876e..68683ff --- a/Classes/ViewHelpers/TrimViewHelper.php +++ b/Classes/ViewHelpers/TrimViewHelper.php @@ -1,5 +1,7 @@ registerArgument('content', 'string', 'Content to be trimmed'); - $this->registerArgument('characters', 'string', 'Characters to be removed'); + $this->registerArgument('characters', 'string', 'Characters to be removed', false); } + /** + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * + * @return string + */ public static function renderStatic( array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext - ): string { - $content = $arguments['content'] ?: $renderChildrenClosure(); - $characters = $arguments['characters'] ?: null; + ) { + $content = $arguments['content'] ? $arguments['content'] : $renderChildrenClosure(); + $characters = $arguments['characters'] ? $arguments['characters'] : null; if ($characters !== null) { $content = trim($content, $characters); diff --git a/Configuration/Icons.php b/Configuration/Icons.php old mode 100755 new mode 100644 diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php old mode 100755 new mode 100644 diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml old mode 100755 new mode 100644 index 87caa87..8050c5b --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -14,5 +14,32 @@ services: description: 'Command to list all content elements for a given subtree' schedulable: false + Evoweb\EwBase\EventListener\CssMerger: + tags: ['event.listener'] + + Evoweb\EwBase\EventListener\JsMerger: + tags: ['event.listener'] + + Evoweb\EwBase\EventListener\IsContentUsedOnPageLayout: + tags: ['event.listener'] + Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem: tags: ['backend.toolbar.item'] + + Evoweb\EwBase\Hooks\UsercentricsPostRenderHook: + public: true + + Evoweb\EwBase\Updates\GridelementsToContainerMigration: + public: true + + Evoweb\EwBase\Updates\GridelementsToContainerService: + public: true + + Evoweb\EwBase\Updates\ParentChildToContainerMigration: + public: true + + Evoweb\EwBase\Updates\ParentChildToContainerService: + public: true + + Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow: + public: true diff --git a/Configuration/TCA/Overrides/pages.php b/Configuration/TCA/Overrides/pages.php old mode 100755 new mode 100644 index 5a8dda2..6a697aa --- a/Configuration/TCA/Overrides/pages.php +++ b/Configuration/TCA/Overrides/pages.php @@ -15,14 +15,14 @@ $newColumns = [ 'suggestOptions' => [ 'default' => [ 'additionalSearchFields' => 'header, bodytext', - 'searchWholePhrase' => false, - ], + 'searchWholePhrase' => false + ] ], 'default' => 0, 'behaviour' => [ - 'allowLanguageSynchronization' => true, - ], - ], + 'allowLanguageSynchronization' => true + ] + ] ], ]; diff --git a/Configuration/Yaml/Csp/Cookiebot.yaml b/Configuration/Yaml/Csp/Cookiebot.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleAdsConversions.yaml b/Configuration/Yaml/Csp/GoogleAdsConversions.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleAdsRemarketing.yaml b/Configuration/Yaml/Csp/GoogleAdsRemarketing.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleAnalytics4.yaml b/Configuration/Yaml/Csp/GoogleAnalytics4.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleAnalytics4Signals.yaml b/Configuration/Yaml/Csp/GoogleAnalytics4Signals.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleAnalytics4SignalsEu.yaml b/Configuration/Yaml/Csp/GoogleAnalytics4SignalsEu.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleOptimize.yaml b/Configuration/Yaml/Csp/GoogleOptimize.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/GoogleTagManagerPreview.yaml b/Configuration/Yaml/Csp/GoogleTagManagerPreview.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/MapBox.yaml b/Configuration/Yaml/Csp/MapBox.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/UniversalAnalytics.yaml b/Configuration/Yaml/Csp/UniversalAnalytics.yaml old mode 100755 new mode 100644 diff --git a/Configuration/Yaml/Csp/Youtube.yaml b/Configuration/Yaml/Csp/Youtube.yaml old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 diff --git a/Resources/Private/Language/locallang_core.xlf b/Resources/Private/Language/locallang_core.xlf old mode 100755 new mode 100644 diff --git a/Resources/Private/Language/locallang_db.xlf b/Resources/Private/Language/locallang_db.xlf old mode 100755 new mode 100644 diff --git a/Resources/Private/Language/locallang_siteconfiguration.xlf b/Resources/Private/Language/locallang_siteconfiguration.xlf old mode 100755 new mode 100644 diff --git a/Resources/Private/Templates/Form/ImageManipulationElement.html b/Resources/Private/Templates/Form/ImageManipulationElement.html old mode 100755 new mode 100644 diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html old mode 100755 new mode 100644 index fea0895..aa01a16 --- a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html +++ b/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html @@ -1,4 +1,6 @@ - + diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html old mode 100755 new mode 100644 diff --git a/Resources/Public/Icons/Extension.svg b/Resources/Public/Icons/Extension.svg old mode 100755 new mode 100644 diff --git a/Resources/Public/Icons/Extension_16.svg b/Resources/Public/Icons/Extension_16.svg old mode 100755 new mode 100644 diff --git a/Resources/Public/Icons/usercentrics.svg b/Resources/Public/Icons/usercentrics.svg old mode 100755 new mode 100644 diff --git a/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css b/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css old mode 100755 new mode 100644 diff --git a/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.js b/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.js old mode 100755 new mode 100644 diff --git a/composer.json b/composer.json old mode 100755 new mode 100644 index 51e0956..d9f7594 --- a/composer.json +++ b/composer.json @@ -1,12 +1,10 @@ { "name": "evoweb/ew-base", "type": "typo3-cms-extension", - "license": ["GPL-2.0-or-later"], - "config": { - "bin-dir": "bin", - "allow-plugins": { - "typo3/class-alias-loader": true, - "typo3/cms-composer-installers": true + "version": "1.0.0", + "autoload": { + "psr-4": { + "Evoweb\\EwBase\\": "Classes/" } }, "require": { @@ -38,24 +36,12 @@ "typo3/cms-tstemplate": "*", "typo3/cms-scheduler": "*", - "helhum/typo3-console": ">8.0", + "helhum/typo3-console": "*", "clickstorm/cs-seo": "*" }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.57.1", - "phpunit/phpunit": "^11.0.3", - "typo3/testing-framework": "dev-main" - }, - "minimum-stability": "dev", - "prefer-stable": true, "extra": { "typo3/cms": { "extension-key": "ew_base" } - }, - "autoload": { - "psr-4": { - "Evoweb\\EwBase\\": "Classes/" - } } } diff --git a/ext_localconf.php b/ext_localconf.php old mode 100755 new mode 100644 index 90c27cd..9f47c15 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -1,6 +1,6 @@ Date: Thu, 26 Dec 2024 21:44:19 +0100 Subject: [PATCH 20/20] [TASK] Cleanup and replace deprecations --- .gitattributes | 7 +- Classes/Command/ContentElementCommand.php | 6 +- .../Configuration/AdditionalConfiguration.php | 32 +- Classes/DataProcessing/ContainerProcessor.php | 58 --- .../DataProcessing/DatabaseQueryProcessor.php | 485 +++++------------- Classes/EventListener/CssMerger.php | 46 +- .../IsContentUsedOnPageLayout.php | 25 - Classes/EventListener/JsMerger.php | 28 +- Classes/EventListener/ReleaseToolbarItem.php | 58 +++ Classes/Form/Element/PickColorFromImage.php | 60 +-- Classes/Hooks/PageLayoutView.php | 34 -- Classes/ToolbarItems/ReleaseToolbarItem.php | 98 ---- .../GridelementsToContainerMigration.php | 96 ---- .../GridelementsToContainerService.php | 268 ---------- .../ParentChildToContainerMigration.php | 106 ---- .../Updates/ParentChildToContainerService.php | 168 ------ Classes/User/AssetPath.php | 11 + Classes/ViewHelpers/Array/AddViewHelper.php | 31 +- .../ViewHelpers/Array/ExplodeViewHelper.php | 29 +- .../ViewHelpers/Be/ThumbnailViewHelper.php | 74 +-- .../Condition/InArrayViewHelper.php | 32 +- .../Condition/StringContainsViewHelper.php | 34 +- .../Context/DevelopmentViewHelper.php | 27 +- .../Context/ProductionViewHelper.php | 27 +- .../ViewHelpers/Context/StagingViewHelper.php | 27 +- .../FixFlexformForExtbaseViewHelper.php | 28 +- Classes/ViewHelpers/FlexFormViewHelper.php | 42 +- Classes/ViewHelpers/HashViewHelper.php | 60 --- .../ViewHelpers/Iterator/AddViewHelper.php | 56 -- .../Iterator/ExplodeViewHelper.php | 93 ---- Classes/ViewHelpers/PublicPathViewHelper.php | 28 +- Classes/ViewHelpers/ReplaceViewHelper.php | 29 +- Classes/ViewHelpers/SvgViewHelper.php | 42 +- Classes/ViewHelpers/TrimViewHelper.php | 30 +- Configuration/Icons.php | 5 - Configuration/Services.yaml | 21 +- .../Sets/EwBase/settings.definitions.yaml | 42 +- Configuration/Sets/EwBase/settings.yaml | 22 +- Configuration/TCA/Overrides/pages.php | 4 +- README.md | 18 - Resources/Private/Language/locallang_core.xlf | 3 - .../Language/locallang_siteconfiguration.xlf | 33 -- .../ToolbarItems/ShowReleaseDropDown.html | 14 - .../ToolbarItems/ShowReleaseToolbarItem.html | 8 - Resources/Public/Icons/usercentrics.svg | 15 - composer.json | 1 - ext_conf_template.txt | 2 - ext_localconf.php | 2 +- ext_tables.sql | 7 - 49 files changed, 540 insertions(+), 1932 deletions(-) delete mode 100644 Classes/DataProcessing/ContainerProcessor.php delete mode 100644 Classes/EventListener/IsContentUsedOnPageLayout.php create mode 100644 Classes/EventListener/ReleaseToolbarItem.php delete mode 100644 Classes/Hooks/PageLayoutView.php delete mode 100644 Classes/ToolbarItems/ReleaseToolbarItem.php delete mode 100644 Classes/Updates/GridelementsToContainerMigration.php delete mode 100644 Classes/Updates/GridelementsToContainerService.php delete mode 100644 Classes/Updates/ParentChildToContainerMigration.php delete mode 100644 Classes/Updates/ParentChildToContainerService.php delete mode 100644 Classes/ViewHelpers/HashViewHelper.php delete mode 100644 Classes/ViewHelpers/Iterator/AddViewHelper.php delete mode 100644 Classes/ViewHelpers/Iterator/ExplodeViewHelper.php delete mode 100644 Resources/Private/Language/locallang_siteconfiguration.xlf delete mode 100644 Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html delete mode 100644 Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html delete mode 100644 Resources/Public/Icons/usercentrics.svg delete mode 100644 ext_conf_template.txt delete mode 100644 ext_tables.sql diff --git a/.gitattributes b/.gitattributes index c1108e7..290f6c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ -/.github/ export-ignore -/Build/ export-ignore -/Tests/ export-ignore +/.github export-ignore +/Build export-ignore +/Tests export-ignore /.gitattributes export-ignore /.gitignore export-ignore +/.gitlab-ci.yml export-ignore diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php index 687e76d..544fcb2 100644 --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -1,6 +1,6 @@ setAliases(['kc-sitepackage']) diff --git a/Classes/Configuration/AdditionalConfiguration.php b/Classes/Configuration/AdditionalConfiguration.php index e6b6165..7998e86 100644 --- a/Classes/Configuration/AdditionalConfiguration.php +++ b/Classes/Configuration/AdditionalConfiguration.php @@ -2,44 +2,22 @@ declare(strict_types=1); -namespace Evoweb\EwBase\Configuration; - /* - * This file is part of TYPO3 CMS-based extension "container" by b13. + * This file is part of TYPO3 CMS-based extension "ew-base" by evoWeb. * * 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. */ -use Mfc\OAuth2\ResourceServer\GitLab; -use Mfc\OAuth2\ResourceServer\Registry; +namespace Evoweb\EwBase\Configuration; + use TYPO3\CMS\Core\Core\Environment; -use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; class AdditionalConfiguration { protected string $extensionKey = 'ew_base'; - protected array $oauthOptions = [ - 'enabled' => true, // Enable/Disable the provider - 'arguments' => [ - 'appId' => '', - 'appSecret' => '', - 'projectName' => '', - 'gitlabServer' => 'https://github.com', - // User level at which the user will be given admin permissions - 'gitlabAdminUserLevel' => 30, - // Groups to assign to the User (comma separated list possible) - 'gitlabDefaultGroups' => 1, - // UserConfig db and/or file mount from groups - 'gitlabUserOption' => 3, - // Blocks users with flag external from access the backend - 'blockExternalUser' => false, - ], - ]; - protected array $developConfig = [ 'BE' => [ 'debug' => true, @@ -70,11 +48,11 @@ class AdditionalConfiguration /** * This is needed to override all mail settings instead of merging them. * - * @var array|string[] + * @var array */ protected array $mailConfig = [ 'transport' => 'smtp', - 'transport_smtp_server' => '127.0.0.1:1025' + 'transport_smtp_server' => 'mailpit:1025' ]; public function initialize(array $configuration = []): void diff --git a/Classes/DataProcessing/ContainerProcessor.php b/Classes/DataProcessing/ContainerProcessor.php deleted file mode 100644 index 6d48d2a..0000000 --- a/Classes/DataProcessing/ContainerProcessor.php +++ /dev/null @@ -1,58 +0,0 @@ -getChildrenByColPos($colPos); - - if (!$processorConfiguration['doNotProcessChildren']) { - $contentRecordRenderer = new RecordsContentObject(); - $conf = [ - 'tables' => 'tt_content' - ]; - foreach ($children as &$child) { - if ($child['l18n_parent'] > 0) { - $conf['source'] = $child['l18n_parent']; - } else { - $conf['source'] = $child['uid']; - } - if ($child['t3ver_oid'] > 0) { - $conf['source'] = $child['t3ver_oid']; - } - $child['renderedContent'] = $cObj->render($contentRecordRenderer, $conf); - /** @var ContentObjectRenderer $recordContentObjectRenderer */ - $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $recordContentObjectRenderer->start($child, 'tt_content'); - $child = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $child); - } - } - - $processedData[$as] = $children; - return $processedData; - } -} diff --git a/Classes/DataProcessing/DatabaseQueryProcessor.php b/Classes/DataProcessing/DatabaseQueryProcessor.php index 057ec66..7f6d5b9 100644 --- a/Classes/DataProcessing/DatabaseQueryProcessor.php +++ b/Classes/DataProcessing/DatabaseQueryProcessor.php @@ -19,15 +19,16 @@ namespace Evoweb\EwBase\DataProcessing; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Result; -use TYPO3\CMS\Backend\FrontendBackendUserAuthentication; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; use TYPO3\CMS\Core\Context\LanguageAspect; +use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Database\Query\QueryHelper; -use TYPO3\CMS\Core\Database\Query\Restriction\DocumentTypeExclusionRestriction; -use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; +use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\TimeTracker\TimeTracker; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -35,7 +36,7 @@ use TYPO3\CMS\Core\Versioning\VersionState; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; -use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException; /** * Fetch records from the database, using the default .select syntax from TypoScript. @@ -63,17 +64,13 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; */ class DatabaseQueryProcessor implements DataProcessorInterface { - protected ContentDataProcessor $contentDataProcessor; - protected ContentObjectRenderer $cObj; - /** - * Constructor - */ - public function __construct() - { - $this->contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class); - } + protected ?ServerRequestInterface $request = null; + + protected string $tableName = ''; + + public function __construct(protected readonly ContentDataProcessor $contentDataProcessor) {} /** * Fetches records from the database as an array @@ -84,14 +81,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) * * @return array the processed data as key/value store + * @throws ContentRenderingException */ public function process( ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData - ) { - $this->cObj = $cObj; + ): array { if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { return $processedData; } @@ -108,6 +105,13 @@ class DatabaseQueryProcessor implements DataProcessorInterface unset($processorConfiguration['table']); } + $this->request = $cObj->getRequest(); + $this->tableName = $tableName; + $this->cObj = clone $cObj; + // @extensionScannerIgnoreLine + $this->cObj->start($cObj->data, $tableName); + $this->cObj->setRequest($this->request); + // The variable to be used within the result $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'records'); @@ -116,8 +120,10 @@ class DatabaseQueryProcessor implements DataProcessorInterface $request = $cObj->getRequest(); $processedRecordVariables = []; foreach ($records as $key => $record) { + /** @var ContentObjectRenderer $recordContentObjectRenderer */ $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $recordContentObjectRenderer->start($record, $tableName, $request); + $recordContentObjectRenderer->setRequest($request); + $recordContentObjectRenderer->start($record, $tableName); $processedRecordVariables[$key] = ['data' => $record]; $processedRecordVariables[$key] = $this->contentDataProcessor->process( $recordContentObjectRenderer, @@ -137,14 +143,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface $statement = $this->exec_getQuery($tableName, $queryConfiguration); - $tsfe = $this->getTypoScriptFrontendController(); + $pageRepository = $this->getPageRepository(); while ($row = $statement->fetchAssociative()) { // Versioning preview: - $tsfe->sys_page->versionOL($tableName, $row, true); + $pageRepository->versionOL($tableName, $row, true); // Language overlay: if (is_array($row)) { - $row = $tsfe->sys_page->getLanguageOverlay($tableName, $row); + $row = $pageRepository->getLanguageOverlay($tableName, $row); } // Might be unset in the language overlay @@ -158,16 +164,15 @@ class DatabaseQueryProcessor implements DataProcessorInterface protected function exec_getQuery(string $table, array $conf): Result { - $statement = $this->getQuery($table, $conf); $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $statement = $this->getQuery($connection, $table, $conf); return $connection->executeQuery($statement); } - public function getQuery($table, $conf, $returnQueryArray = false) + public function getQuery(Connection $connection, $table, $conf): string { // Resolve stdWrap in these properties first - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); $properties = [ 'pidInList', 'uidInList', @@ -200,7 +205,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface } } // Handle PDO-style named parameter markers first - $queryMarkers = $this->getQueryMarkers($table, $conf); + $queryMarkers = $this->cObj->getQueryMarkers($connection, $conf); // Replace the markers in the non-stdWrap properties foreach ($queryMarkers as $marker => $markerValue) { $properties = [ @@ -217,7 +222,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface ]; foreach ($properties as $property) { if ($conf[$property] ?? false) { - $conf[$property] = str_replace('###' . $marker . '###', (string)$markerValue, $conf[$property]); + $conf[$property] = str_replace('###' . $marker . '###', $markerValue, $conf[$property]); } } } @@ -230,10 +235,11 @@ class DatabaseQueryProcessor implements DataProcessorInterface $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true); array_walk($pidList, function (&$storagePid) { if ($storagePid === 'this') { - $storagePid = $this->getTypoScriptFrontendController()->id; + $storagePid = $this->getRequest()->getAttribute('frontend.page.information')->getId(); } }); - $expandedPidList = $this->getTypoScriptFrontendController()->sys_page->getPageIdsRecursive($pidList, $conf['recursive']); + $pageRepository = $this->getPageRepository(); + $expandedPidList = $pageRepository->getPageIdsRecursive($pidList, $conf['recursive']); $conf['pidInList'] = implode(',', $expandedPidList); } } @@ -241,7 +247,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface $conf['pidInList'] = 'this'; } - $queryParts = $this->getQueryConstraints($table, $conf); + $queryParts = $this->getQueryConstraints($connection, $table, $conf); $queryBuilder = $connection->createQueryBuilder(); // @todo Check against getQueryConstraints, can probably use FrontendRestrictions @@ -253,7 +259,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface $queryBuilder->where($queryParts['where']); } - if ($queryParts['groupBy'] ?? false) { + if (($queryParts['groupBy'] ?? false) && is_array($queryParts['groupBy'])) { $queryBuilder->groupBy(...$queryParts['groupBy']); } @@ -265,15 +271,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface // Fields: if ($conf['selectFields'] ?? false) { - $queryBuilder->selectLiteral($this->sanitizeSelectPart($conf['selectFields'], $table)); + $queryBuilder->selectLiteral($this->sanitizeSelectPart($connection, $conf['selectFields'], $table)); } // Setting LIMIT: - $error = false; if (($conf['max'] ?? false) || ($conf['begin'] ?? false)) { // Finding the total number of records, if used: if (str_contains(strtolower(($conf['begin'] ?? '') . ($conf['max'] ?? '')), 'total')) { - $countQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $countQueryBuilder = $connection->createQueryBuilder(); $countQueryBuilder->getRestrictions()->removeAll(); $countQueryBuilder->count('*') ->from($table) @@ -293,237 +298,77 @@ class DatabaseQueryProcessor implements DataProcessorInterface } } catch (DBALException $e) { $this->getTimeTracker()->setTSlogMessage($e->getPrevious()->getMessage()); - $error = true; + return ''; } } - if (!$error) { - if (isset($conf['begin']) && $conf['begin'] > 0) { - $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['begin'])), 0); - $queryBuilder->setFirstResult($conf['begin']); - } - if (isset($conf['max'])) { - $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['max'])), 0); - $queryBuilder->setMaxResults($conf['max'] ?: 100000); - } + if (isset($conf['begin']) && $conf['begin'] > 0) { + $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->cObj->calc($conf['begin'])), 0); + $queryBuilder->setFirstResult($conf['begin']); + } + if (isset($conf['max'])) { + $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->cObj->calc($conf['max'])), 0); + $queryBuilder->setMaxResults($conf['max'] ?: 100000); } } - if (!$error) { - // Setting up tablejoins: - if ($conf['join'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['join']); - $queryBuilder->join( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } elseif ($conf['leftjoin'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['leftjoin']); - $queryBuilder->leftJoin( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } elseif ($conf['rightjoin'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['rightjoin']); - $queryBuilder->rightJoin( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } - - // Convert the QueryBuilder object into a SQL statement. - $query = $queryBuilder->getSQL(); - - // Replace the markers in the queryParts to handle stdWrap enabled properties - foreach ($queryMarkers as $marker => $markerValue) { - // @todo Ugly hack that needs to be cleaned up, with the current architecture - // @todo for exec_Query / getQuery it's the best we can do. - $query = str_replace('###' . $marker . '###', (string)$markerValue, $query); - } - - return $returnQueryArray ? $this->getQueryArray($queryBuilder) : $query; + // Setting up tablejoins: + if ($conf['join'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['join']); + $queryBuilder->join( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['leftjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['leftjoin']); + $queryBuilder->leftJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['rightjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['rightjoin']); + $queryBuilder->rightJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); } - return ''; - } + // Convert the QueryBuilder object into a SQL statement. + $query = $queryBuilder->getSQL(); - /** - * Helper to transform a QueryBuilder object into a queryParts array that can be used - * with exec_SELECT_queryArray - * - * @return array - * @throws \RuntimeException - */ - protected function getQueryArray(QueryBuilder $queryBuilder) - { - $fromClauses = []; - $knownAliases = []; - $queryParts = []; - - // Loop through all FROM clauses - foreach ($queryBuilder->getQueryPart('from') as $from) { - if ($from['alias'] === null) { - $tableSql = $from['table']; - $tableReference = $from['table']; - } else { - $tableSql = $from['table'] . ' ' . $from['alias']; - $tableReference = $from['alias']; - } - - $knownAliases[$tableReference] = true; - - $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper( - $tableReference, - $queryBuilder->getQueryPart('join'), - $knownAliases - ); + // Replace the markers in the queryParts to handle stdWrap enabled properties + foreach ($queryMarkers as $marker => $markerValue) { + // @todo Ugly hack that needs to be cleaned up, with the current architecture + // @todo for exec_Query / getQuery it's the best we can do. + $query = str_replace('###' . $marker . '###', (string)$markerValue, $query); } - $queryParts['SELECT'] = implode(', ', $queryBuilder->getQueryPart('select')); - $queryParts['FROM'] = implode(', ', $fromClauses); - $queryParts['WHERE'] = (string)$queryBuilder->getQueryPart('where') ?: ''; - $queryParts['GROUPBY'] = implode(', ', $queryBuilder->getQueryPart('groupBy')); - $queryParts['ORDERBY'] = implode(', ', $queryBuilder->getQueryPart('orderBy')); - if ($queryBuilder->getFirstResult() > 0) { - $queryParts['LIMIT'] = $queryBuilder->getFirstResult() . ',' . $queryBuilder->getMaxResults(); - } elseif ($queryBuilder->getMaxResults() > 0) { - $queryParts['LIMIT'] = $queryBuilder->getMaxResults(); - } - - return $queryParts; - } - - /** - * Helper to transform the QueryBuilder join part into a SQL fragment. - * - * @throws \RuntimeException - */ - protected function getQueryArrayJoinHelper(string $fromAlias, array $joinParts, array &$knownAliases): string - { - $sql = ''; - - if (isset($joinParts['join'][$fromAlias])) { - foreach ($joinParts['join'][$fromAlias] as $join) { - if (array_key_exists($join['joinAlias'], $knownAliases)) { - throw new \RuntimeException( - 'Non unique join alias: "' . $join['joinAlias'] . '" found.', - 1472748872 - ); - } - $sql .= ' ' . strtoupper($join['joinType']) - . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] - . ' ON ' . ((string)$join['joinCondition']); - $knownAliases[$join['joinAlias']] = true; - } - - foreach ($joinParts['join'][$fromAlias] as $join) { - $sql .= $this->getQueryArrayJoinHelper($join['joinAlias'], $joinParts, $knownAliases); - } - } - - return $sql; - } - - /** - * Builds list of marker values for handling PDO-like parameter markers in select parts. - * Marker values support stdWrap functionality thus allowing a way to use stdWrap functionality in various - * properties of 'select' AND prevents SQL-injection problems by quoting and escaping of numeric values, strings, - * NULL values and comma separated lists. - * - * @param string $table Table to select records from - * @param array $conf Select part of CONTENT definition - * @return array List of values to replace markers with - * @internal - * @see getQuery() - */ - public function getQueryMarkers(string $table, array $conf): array - { - if (!isset($conf['markers.']) || !is_array($conf['markers.'])) { - return []; - } - // Parse markers and prepare their values - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); - $markerValues = []; - foreach ($conf['markers.'] as $dottedMarker => $dummy) { - $marker = rtrim($dottedMarker, '.'); - if ($dottedMarker != $marker . '.') { - continue; - } - // Parse definition - // todo else value is always null - $tempValue = isset($conf['markers.'][$dottedMarker]) - ? $this->cObj->stdWrap($conf['markers.'][$dottedMarker]['value'] ?? '', $conf['markers.'][$dottedMarker]) - : $conf['markers.'][$dottedMarker]['value']; - // Quote/escape if needed - if (is_numeric($tempValue)) { - if ((int)$tempValue == $tempValue) { - // Handle integer - $markerValues[$marker] = (int)$tempValue; - } else { - // Handle float - $markerValues[$marker] = (float)$tempValue; - } - } elseif ($tempValue === null) { - // It represents NULL - $markerValues[$marker] = 'NULL'; - } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) { - // See if it is really a comma separated list of values - $explodeValues = GeneralUtility::trimExplode(',', $tempValue); - if (count($explodeValues) > 1) { - // Handle each element of list separately - $tempArray = []; - foreach ($explodeValues as $listValue) { - if (is_numeric($listValue)) { - if ((int)$listValue == $listValue) { - $tempArray[] = (int)$listValue; - } else { - $tempArray[] = (float)$listValue; - } - } else { - // If quoted, remove quotes before - // escaping. - if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) { - $listValue = $matches[1]; - } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) { - $listValue = $matches[1]; - } - $tempArray[] = $connection->quote($listValue); - } - } - $markerValues[$marker] = implode(',', $tempArray); - } else { - // Handle remaining values as string - $markerValues[$marker] = $connection->quote($tempValue); - } - } else { - // Handle remaining values as string - $markerValues[$marker] = $connection->quote($tempValue); - } - } - return $markerValues; + return $query; } /** * Helper function for getQuery(), creating the WHERE clause of the SELECT query * + * @param Connection $connection * @param string $table The table name * @param array $conf The TypoScript configuration properties * @return array Associative array containing the prepared data for WHERE, ORDER BY and GROUP BY fragments - * @throws \InvalidArgumentException + * @throws AspectNotFoundException + * @throws ContentRenderingException * @see getQuery() */ - protected function getQueryConstraints(string $table, array $conf): array + protected function getQueryConstraints(Connection $connection, string $table, array $conf): array { - // Init: - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder = $connection->createQueryBuilder(); $expressionBuilder = $queryBuilder->expr(); - $tsfe = $this->getTypoScriptFrontendController(); + $request = $this->getRequest(); + $contentPid = $request->getAttribute('frontend.page.information')->getContentFromPid(); $constraints = []; $pid_uid_flag = 0; $enableFieldsIgnore = []; @@ -540,16 +385,16 @@ class DatabaseQueryProcessor implements DataProcessorInterface ); if (trim($conf['uidInList'] ?? '')) { - $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['uidInList'])); + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['uidInList'])); // If moved records shall be considered, select via t3ver_oid if ($considerMovePointers) { - $constraints[] = (string)$expressionBuilder->orX( + $constraints[] = (string)$expressionBuilder->or( $expressionBuilder->in($table . '.uid', $listArr), - $expressionBuilder->andX( + $expressionBuilder->and( $expressionBuilder->eq( $table . '.t3ver_state', - (int)(string)VersionState::cast(VersionState::MOVE_POINTER) + VersionState::MOVE_POINTER->value ), $expressionBuilder->in($table . '.t3ver_oid', $listArr) ) @@ -561,14 +406,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface } // Static_* tables are allowed to be fetched from root page - if (strpos($table, 'static_') === 0) { + if (str_starts_with($table, 'static_')) { $pid_uid_flag++; } if (trim($conf['pidInList'])) { - $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['pidInList'])); + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['pidInList'])); // Removes all pages which are not visible for the user! - $listArr = $this->checkPidArray($listArr); + $listArr = $this->cObj->checkPidArray($listArr); if (GeneralUtility::inList($conf['pidInList'], 'root')) { $listArr[] = 0; } @@ -586,11 +431,11 @@ class DatabaseQueryProcessor implements DataProcessorInterface } // If not uid and not pid then uid is set to 0 - which results in nothing!! - if (!$pid_uid_flag && trim($conf['pidInList'] ?? '') != 'ignore') { + if (!$pid_uid_flag && trim($conf['pidInList'] ?? '') !== 'ignore') { $constraints[] = $expressionBuilder->eq($table . '.uid', 0); } - $where = trim((string)$this->cObj->stdWrapValue('where', $conf ?? [])); + $where = trim((string)$this->cObj->stdWrapValue('where', $conf)); if ($where) { $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where); } @@ -602,26 +447,25 @@ class DatabaseQueryProcessor implements DataProcessorInterface $constraints[] = $languageConstraint; } - // Enablefields - if ($table === 'pages') { - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_hid_del); - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_groupAccess); - } else { - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->enableFields($table, -1, $enableFieldsIgnore)); - } + // default constraints from TCA + $pageRepository = $this->getPageRepository(); + $constraints = array_merge( + $constraints, + array_values($pageRepository->getDefaultConstraints($table, $enableFieldsIgnore)) + ); // MAKE WHERE: - if (count($constraints) !== 0) { - $queryParts['where'] = $expressionBuilder->andX(...$constraints); + if ($constraints !== []) { + $queryParts['where'] = $expressionBuilder->and(...$constraints); } // GROUP BY - $groupBy = trim((string)$this->cObj->stdWrapValue('groupBy', $conf ?? [])); + $groupBy = trim((string)$this->cObj->stdWrapValue('groupBy', $conf)); if ($groupBy) { $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy); } // ORDER BY - $orderByString = trim((string)$this->cObj->stdWrapValue('orderBy', $conf ?? [])); + $orderByString = trim((string)$this->cObj->stdWrapValue('orderBy', $conf)); if ($orderByString) { $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString); } @@ -630,26 +474,6 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $queryParts; } - /** - * Removes Page UID numbers from the input array which are not available due to enableFields() or the list of bad doktype numbers ($this->checkPid_badDoktypeList) - * - * @param int[] $pageIds Array of Page UID numbers for select and for which pages with enablefields and bad doktypes should be removed. - * @return array Returns the array of remaining page UID numbers - * @internal - */ - public function checkPidArray(array $pageIds): array - { - if (empty($pageIds)) { - return []; - } - $restrictionContainer = GeneralUtility::makeInstance(FrontendRestrictionContainer::class); - $restrictionContainer->add(GeneralUtility::makeInstance( - DocumentTypeExclusionRestriction::class, - GeneralUtility::intExplode(',', (string)$this->cObj->checkPid_badDoktypeList, true) - )); - return $this->getTypoScriptFrontendController()->sys_page->filterAccessiblePageIds($pageIds, $restrictionContainer); - } - /** * Adds parts to the WHERE clause that are related to language. * This only works on TCA tables which have the [ctrl][languageField] field set or if they @@ -669,15 +493,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface * If the language aspect has NO overlays enabled, it behaves as in "free mode" (= only fetch the records * for the current language. * - * @param ExpressionBuilder $expressionBuilder - * @param string $table - * @param array $conf - * @param Context $context - * @return string|\TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression|null - * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException + * @throws AspectNotFoundException */ - protected function getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context) - { + protected function getLanguageRestriction( + ExpressionBuilder $expressionBuilder, + string $table, + array $conf, + Context $context + ): string|CompositeExpression|null { $languageField = ''; $localizationParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? null; // Check if the table is translatable, and set the language field by default from the TCA information @@ -706,15 +529,16 @@ class DatabaseQueryProcessor implements DataProcessorInterface $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ? $this->cObj->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation']; - $includeRecordsWithoutDefaultTranslation = trim($includeRecordsWithoutDefaultTranslation) !== ''; + $includeRecordsWithoutDefaultTranslation = trim((string)$includeRecordsWithoutDefaultTranslation); + $includeRecordsWithoutDefaultTranslation = $includeRecordsWithoutDefaultTranslation !== '' && $includeRecordsWithoutDefaultTranslation !== '0'; } else { // Option was not explicitly set, check what's in for the language overlay type. $includeRecordsWithoutDefaultTranslation = $languageAspect->getOverlayType() === $languageAspect::OVERLAYS_ON_WITH_FLOATING; } if ($includeRecordsWithoutDefaultTranslation) { - $languageQuery = $expressionBuilder->orX( + $languageQuery = $expressionBuilder->or( $languageQuery, - $expressionBuilder->andX( + $expressionBuilder->and( $expressionBuilder->eq($table . '.' . $localizationParentField, 0), $expressionBuilder->eq($languageField, $languageAspect->getContentId()) ) @@ -723,7 +547,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $languageQuery; } // No overlays = only fetch records given for the requested language and "all languages" - return $expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]); + return (string)$expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]); } /** @@ -732,21 +556,18 @@ class DatabaseQueryProcessor implements DataProcessorInterface * This functions checks if the necessary fields are part of the select * and adds them if necessary. * - * @param string $selectPart Select part - * @param string $table Table to select from * @return string Sanitized select part * @internal * @see getQuery */ - protected function sanitizeSelectPart(string $selectPart, string $table): string + protected function sanitizeSelectPart(Connection $connection, string $selectPart, string $table): string { - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); - // Pattern matching parts $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)'; $matchEnd = '(\\s*,|\\s*$)/'; $necessaryFields = ['uid', 'pid']; $wsFields = ['t3ver_state']; + $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? false; if (isset($GLOBALS['TCA'][$table]) && !preg_match($matchStart . '\\*' . $matchEnd, $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)|distinct/i', $selectPart)) { foreach ($necessaryFields as $field) { $match = $matchStart . $field . $matchEnd; @@ -754,6 +575,12 @@ class DatabaseQueryProcessor implements DataProcessorInterface $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field); } } + if (is_string($languageField)) { + $match = $matchStart . $languageField . $matchEnd; + if (!preg_match($match, $selectPart)) { + $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $languageField) . ' AS ' . $connection->quoteIdentifier($languageField); + } + } if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ?? false) { foreach ($wsFields as $field) { $match = $matchStart . $field . $matchEnd; @@ -766,66 +593,24 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $selectPart; } - /** - * Performs basic mathematical evaluation of the input string. Does NOT take parenthesis and operator precedence into account! (for that, see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction()) - * - * @param string $val The string to evaluate. Example: "3+4*10/5" will generate "35". Only integer numbers can be used. - * @return int The result (might be a float if you did a division of the numbers). - * @see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction() - */ - public function calc($val) + protected function getPageRepository(): PageRepository { - $parts = GeneralUtility::splitCalc($val, '+-*/'); - $value = 0; - foreach ($parts as $part) { - $theVal = $part[1]; - $sign = $part[0]; - if ((string)(int)$theVal === (string)$theVal) { - $theVal = (int)$theVal; - } else { - $theVal = 0; - } - if ($sign === '-') { - $value -= $theVal; - } - if ($sign === '+') { - $value += $theVal; - } - if ($sign === '/') { - if ((int)$theVal) { - $value /= (int)$theVal; - } - } - if ($sign === '*') { - $value *= $theVal; - } - } - return $value; + return GeneralUtility::makeInstance(PageRepository::class); } - /** - * @return TimeTracker - */ - protected function getTimeTracker() + protected function getTimeTracker(): TimeTracker { return GeneralUtility::makeInstance(TimeTracker::class); } - /** - * Returns the current BE user. - * - * @return FrontendBackendUserAuthentication - */ - protected function getFrontendBackendUser() + public function getRequest(): ServerRequestInterface { - return $GLOBALS['BE_USER']; - } - - /** - * @return TypoScriptFrontendController|null - */ - protected function getTypoScriptFrontendController() - { - return $GLOBALS['TSFE'] ?? null; + if ($this->request instanceof ServerRequestInterface) { + return $this->request; + } + throw new ContentRenderingException( + 'PSR-7 request is missing in ContentObjectRenderer. Inject with start(), setRequest() or provide via $GLOBALS[\'TYPO3_REQUEST\'].', + 1607172972 + ); } } diff --git a/Classes/EventListener/CssMerger.php b/Classes/EventListener/CssMerger.php index dfd8de0..ab6003b 100644 --- a/Classes/EventListener/CssMerger.php +++ b/Classes/EventListener/CssMerger.php @@ -1,17 +1,33 @@ extensionConfiguration->get('ew_base', 'inlineCssStyles'); - if ($inlineCssStyles) { + /** @var Site $site */ + $site = $this->getRequest()->getAttribute('site'); + if ( + $site instanceof Site + && ($siteSettings = $site->getSettings()) + && !$siteSettings->isEmpty() + && $siteSettings->get('ew-base.inlineCssStyles') + ) { $assetCollector->addInlineStyleSheet('ew_base', $styles, [], ['priority' => true]); } else { $temporaryFile = GeneralUtility::writeStyleSheetContentToTemporaryFile($styles); - $assetCollector->addStyleSheet('combined_styles', $temporaryFile); + // @extensionScannerIgnoreLine + $assetCollector->addStyleSheet( + 'ew_base_combined_styles', + $temporaryFile, + ['nounce' => $this->requestId->nonce] + ); } } } @@ -96,4 +123,9 @@ class CssMerger $content ); } + + protected function getRequest(): ServerRequestInterface + { + return $GLOBALS['TYPO3_REQUEST']; + } } diff --git a/Classes/EventListener/IsContentUsedOnPageLayout.php b/Classes/EventListener/IsContentUsedOnPageLayout.php deleted file mode 100644 index daafc1e..0000000 --- a/Classes/EventListener/IsContentUsedOnPageLayout.php +++ /dev/null @@ -1,25 +0,0 @@ -setUsed($event->isRecordUsed() || $this->findCTypeBegin($event->getRecord()['CType'])); - } - - public function findCTypeBegin($cType): bool - { - $found = false; - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { - if (strpos($cType, $search) !== false) { - $found = true; - break; - } - } - return $found; - } -} diff --git a/Classes/EventListener/JsMerger.php b/Classes/EventListener/JsMerger.php index 52d7381..8fd6092 100644 --- a/Classes/EventListener/JsMerger.php +++ b/Classes/EventListener/JsMerger.php @@ -1,14 +1,32 @@ isInline() && $event->isPriority()) { @@ -22,7 +40,11 @@ class JsMerger $scripts = trim(implode(LF, $scripts)); if (!empty($scripts)) { $temporaryFile = GeneralUtility::writeJavaScriptContentToTemporaryFile($scripts); - $assetCollector->addJavaScript('combined_scripts', $temporaryFile); + $assetCollector->addJavaScript( + 'ew_base_combined_scripts', + $temporaryFile, + ['nounce' => $this->requestId->nonce] + ); } } } @@ -90,7 +112,7 @@ class JsMerger 'url("' . $relativeFilePath . '/..', 'url(\'' . $relativeFilePath . '/..', ], - $content + $content, ); } } diff --git a/Classes/EventListener/ReleaseToolbarItem.php b/Classes/EventListener/ReleaseToolbarItem.php new file mode 100644 index 0000000..e316acc --- /dev/null +++ b/Classes/EventListener/ReleaseToolbarItem.php @@ -0,0 +1,58 @@ +getToolbarItem(); + + $release = $this->getRelease(); + if ($release !== []) { + $label = $this->getLanguageService()->sL( + 'LLL:EXT:ew_base/Resources/Private/Language/locallang_core.xlf:rm.release_label-' + . ($release['isTag'] ? 'tag' : 'branch') + ); + $systemInformationToolbarItem->addSystemInformation( + $label, + $release['release'], + 'information-git', + InformationStatus::OK + ); + } + } + + protected function getRelease(): array + { + $release = GeneralUtility::getUrl(Environment::getProjectPath() . '/release'); + return $release ? [ + 'release' => trim($release), + 'isTag' => str_contains($release, '.'), + ] : []; + } + + private function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } +} diff --git a/Classes/Form/Element/PickColorFromImage.php b/Classes/Form/Element/PickColorFromImage.php index 705918b..38eb3f8 100644 --- a/Classes/Form/Element/PickColorFromImage.php +++ b/Classes/Form/Element/PickColorFromImage.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Evoweb\EwBase\Form\Element; - /* * This file is developed by evoWeb. * @@ -15,11 +13,14 @@ namespace Evoweb\EwBase\Form\Element; * LICENSE.txt file that was distributed with this source code. */ +namespace Evoweb\EwBase\Form\Element; + use TYPO3\CMS\Backend\Form\Element\AbstractFormElement; use TYPO3\CMS\Backend\View\BackendViewFactory; use TYPO3\CMS\Core\Imaging\ImageManipulation\Area; use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; use TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException; +use TYPO3\CMS\Core\Imaging\ImageManipulation\Ratio; use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; use TYPO3\CMS\Core\Resource\File; @@ -127,15 +128,12 @@ class PickColorFromImage extends AbstractFormElement $config = $this->processConfiguration($config, $parameterArray['itemFormElValue'], $file); $fieldInformationResult = $this->renderFieldInformation(); - $fieldInformationHtml = $fieldInformationResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); $fieldControlResult = $this->renderFieldControl(); - $fieldControlHtml = $fieldControlResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false); $fieldWizardResult = $this->renderFieldWizard(); - $fieldWizardHtml = $fieldWizardResult['html']; $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); $width = $this->formMaxWidth( @@ -147,9 +145,9 @@ class PickColorFromImage extends AbstractFormElement ); $arguments = [ - 'fieldInformation' => $fieldInformationHtml, - 'fieldControl' => $fieldControlHtml, - 'fieldWizard' => $fieldWizardHtml, + 'fieldInformation' => $fieldInformationResult['html'], + 'fieldControl' => $fieldControlResult['html'], + 'fieldWizard' => $fieldWizardResult['html'], 'isAllowedFileExtension' => in_array( strtolower($file->getExtension()), GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), @@ -185,10 +183,8 @@ class PickColorFromImage extends AbstractFormElement $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); } } - $view = $this->backendViewFactory->create($this->data['request']); - $templatePaths = $view->getRenderingContext()->getTemplatePaths(); - $templatePaths->setTemplateRootPaths(['EXT:ew_base/Resources/Private/Templates']); - $templatePaths->setPartialRootPaths(['EXT:ew_base/Resources/Private/Partials']); + + $view = $this->backendViewFactory->create($this->data['request'], ['evoweb/ew-base']); $view->assignMultiple($arguments); $resultArray['html'] = $view->render('Form/ImageManipulationElement'); @@ -211,12 +207,19 @@ class PickColorFromImage extends AbstractFormElement return $file; } + /** + * @throws InvalidConfigurationException + */ protected function populateConfiguration(array $baseConfiguration): array { $defaultConfig = self::$defaultConfig; + + // If ratios are set do not add default options + if (isset($baseConfiguration['cropVariants'])) { + unset($defaultConfig['cropVariants']); + } + $config = array_replace_recursive($defaultConfig, $baseConfiguration); - $imageConfig = $this->data['processedTca']['columns'][$config['imageField']]; - $config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants']; if (!is_array($config['cropVariants'])) { throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267); @@ -225,12 +228,17 @@ class PickColorFromImage extends AbstractFormElement $cropVariants = []; foreach ($config['cropVariants'] as $id => $cropVariant) { // Filter allowed aspect ratios - $cropVariant['allowedAspectRatios'] = array_filter( - $cropVariant['allowedAspectRatios'] ?? [], - static function ($aspectRatio) { - return !(bool)($aspectRatio['disabled'] ?? false); - } - ); + $cropVariant['allowedAspectRatios'] = array_filter($cropVariant['allowedAspectRatios'] ?? [], static function (array $aspectRatio): bool { + return !(bool)($aspectRatio['disabled'] ?? false); + }); + + // Aspect ratios may not contain a "." character, see Ratio::__construct() + // To match them again properly, same replacement is required here. + $preparedAllowedAspectRatios = []; + foreach ($cropVariant['allowedAspectRatios'] as $aspectRatio => $aspectRatioDefinition) { + $preparedAllowedAspectRatios[Ratio::prepareAspectRatioId($aspectRatio)] = $aspectRatioDefinition; + } + $cropVariant['allowedAspectRatios'] = $preparedAllowedAspectRatios; // Ignore disabled crop variants if (!empty($cropVariant['disabled'])) { @@ -238,10 +246,7 @@ class PickColorFromImage extends AbstractFormElement } if (empty($cropVariant['allowedAspectRatios'])) { - throw new InvalidConfigurationException( - 'Crop variants configuration ' . $id . ' contains no allowed aspect ratios', - 1620147893 - ); + throw new InvalidConfigurationException('Crop variants configuration ' . $id . ' contains no allowed aspect ratios', 1620147893); } // Enforce a crop area (default is full image) @@ -254,8 +259,8 @@ class PickColorFromImage extends AbstractFormElement $config['cropVariants'] = $cropVariants; + // By default we allow all image extensions that can be handled by the GFX functionality $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; - return $config; } @@ -267,10 +272,7 @@ class PickColorFromImage extends AbstractFormElement $elementValue = (string)$cropVariantCollection; } $config['cropVariants'] = $cropVariantCollection->asArray(); - $config['allowedExtensions'] = implode( - ', ', - GeneralUtility::trimExplode(',', $config['allowedExtensions'], true) - ); + $config['allowedExtensions'] = implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions'], true)); return $config; } } diff --git a/Classes/Hooks/PageLayoutView.php b/Classes/Hooks/PageLayoutView.php deleted file mode 100644 index 608ccf1..0000000 --- a/Classes/Hooks/PageLayoutView.php +++ /dev/null @@ -1,34 +0,0 @@ -findCTypeBegin($params['record']['CType']); - } - - public function findCTypeBegin($cType): bool - { - $found = false; - foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) { - if (str_contains($cType, $search)) { - $found = true; - break; - } - } - return $found; - } -} diff --git a/Classes/ToolbarItems/ReleaseToolbarItem.php b/Classes/ToolbarItems/ReleaseToolbarItem.php deleted file mode 100644 index 4f01aa4..0000000 --- a/Classes/ToolbarItems/ReleaseToolbarItem.php +++ /dev/null @@ -1,98 +0,0 @@ -request = $request; - } - - public function checkAccess(): bool - { - $result = false; - $backendUser = $this->getBackendUser(); - if ($backendUser->isAdmin()) { - $result = true; - } - if ($backendUser->getTSConfig()['options.']['showSystemInformation'] ?? false) { - $result = true; - } - $release = $this->getRelease(); - if (empty($release)) { - $result = false; - } - return $result; - } - - public function getItem(): string - { - $view = $this->backendViewFactory->create($this->request, ['typo3/cms-backend', 'evoweb/ew-base']); - return $view->render('ToolbarItems/ShowReleaseToolbarItem'); - } - - public function hasDropDown(): bool - { - return true; - } - - public function getDropDown(): string - { - $view = $this->backendViewFactory->create($this->request, ['typo3/cms-backend', 'evoweb/ew-base']); - $view->assignMultiple([ - 'release' => $this->getRelease(), - ]); - return $view->render('ToolbarItems/ShowReleaseDropDown'); - } - - public function getAdditionalAttributes(): array - { - return []; - } - - public function getIndex(): int - { - return 20; - } - - protected function getRelease(): array - { - $release = GeneralUtility::getUrl(Environment::getProjectPath() . '/release'); - return $release ? [ - 'release' => trim($release), - 'isTag' => str_contains($release, '.'), - ] : []; - } - - protected function getBackendUser(): BackendUserAuthentication - { - return $GLOBALS['BE_USER']; - } -} diff --git a/Classes/Updates/GridelementsToContainerMigration.php b/Classes/Updates/GridelementsToContainerMigration.php deleted file mode 100644 index 9a205cb..0000000 --- a/Classes/Updates/GridelementsToContainerMigration.php +++ /dev/null @@ -1,96 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - public function getTitle(): string - { - return 'Migrate gridelements to container'; - } - - public function getDescription(): string - { - return 'Migrates content elements that are type gridelements to corresponding container elements'; - } - - public function getPrerequisites(): array - { - return [ - DatabaseUpdatedPrerequisite::class - ]; - } - - public function updateNecessary(): bool - { - return $this->hasRecordsToUpdate(); - } - - public function executeUpdate(): bool - { - $updateSuccessful = true; - try { - $this->service->migrate(); - } catch (\Exception $exception) { - $this->logger->log(LogLevel::ERROR, $exception->getMessage()); - $updateSuccessful = false; - } - - return $updateSuccessful; - } - - protected function hasRecordsToUpdate(): bool - { - return (bool)$this->getPreparedQueryBuilder()->count('uid')->executeQuery()->fetchOne(); - } - - protected function getPreparedQueryBuilder(): QueryBuilder - { - $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - $queryBuilder - ->from(self::TABLE_NAME) - ->where( - $queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')) - ); - return $queryBuilder; - } - - protected function getConnectionPool(): ConnectionPool - { - return GeneralUtility::makeInstance(ConnectionPool::class); - } -} diff --git a/Classes/Updates/GridelementsToContainerService.php b/Classes/Updates/GridelementsToContainerService.php deleted file mode 100644 index 4b0cdac..0000000 --- a/Classes/Updates/GridelementsToContainerService.php +++ /dev/null @@ -1,268 +0,0 @@ - [ - 'CType' => 'three-col-columns', - ], - '140' => [ - 'CType' => [ - 'search' => 'pi_flexform/type', - 'matches' => [ - 0 => 'two-col-columns-11', - 1 => 'two-col-columns-12', - 2 => 'two-col-columns-21', - ], - ], - 'map' => [ - 'pi_flexform/row_class' => 'frame_class', - ] - ], -]; -*/ - -class GridelementsToContainerService -{ - private const TABLE_NAME = 'tt_content'; - - protected array $resolveContainer = []; - - protected int $colPosOffset = 0; - - protected array $configuration = []; - - public function __construct( - protected ConnectionPool $connectionPool, - protected DataHandler $dataHandler, - protected FlexFormService $flexFormService, - ) { - $config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; - $this->resolveContainer = $config['migrationResolveContainer'] ?? []; - $this->colPosOffset = $config['migrationColPosOffset'] ?? 0; - $this->configuration = $config['migrationMapping'] ?? []; - } - - public function migrate(): void - { - $this->initializeDataHandler(); - - // Move children out of container and remove container - foreach ($this->resolveContainer as $containerId) { - $this->resolveContainers((string)$containerId); - } - - $this->migrateConfiguredContainer(); - } - - protected function initializeDataHandler(): void - { - $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); - $backendUser->user = [ - 'uid' => 0, - 'admin' => 1, - ]; - $this->dataHandler->start([], [], $backendUser); - } - - - protected function resolveContainers(string $layout): void - { - $containers = $this->getGridElementsByLayout($layout); - foreach ($containers as $container) { - $this->processContainerResolve($container); - } - } - - protected function processContainerResolve(array $container): void - { - $children = $this->getGridContainerChildren($container['uid']); - // move first child after container - $moveAfterThis = $container; - foreach ($children as $child) { - [$moveAfterThis, $container] = $this->processContainerResolveChild($child, $moveAfterThis, $container); - } - - $this->deleteElement($container['uid']); - } - - protected function processContainerResolveChild(array $child, array $moveAfterThis, array $container): array - { - $this->updateElement( - $child['uid'], - [ - 'tx_gridelements_container' => 0, - 'colPos' => $container['colPos'], - 'header' => $child['header'] ?: $container['header'] - ] - ); - $this->moveElementAfterElement($child['uid'], $moveAfterThis['uid']); - // use this child to move the next child after - $moveAfterThis = $child; - // empty container header so only the first child gets the header - $container['header'] = ''; - - return [$moveAfterThis, $container]; - } - - protected function deleteElement(int $uid): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, ['delete' => 1], ['uid' => $uid]); - } - - protected function moveElementAfterElement(int $elementToMove, int $elementToMoveAfter): void - { - $this->dataHandler->moveRecord(self::TABLE_NAME, $elementToMove, $elementToMoveAfter * -1); - } - - - protected function migrateConfiguredContainer(): void - { - array_walk($this->configuration, function($config, $key) { - $containers = $this->getGridElementsByLayout((string)$key); - foreach ($containers as $container) { - $container['pi_flexform'] = $this->flexFormService->convertFlexFormContentToArray( - $container['pi_flexform'] - ); - $this->processContainerMigration($container, $config); - } - }); - } - - protected function processContainerMigration(array $container, array $config): void - { - $children = $this->getGridContainerChildren($container['uid']); - foreach ($children as $child) { - $this->processContainerMigrationChild($child, $container); - } - - $data = [ - 'CType' => $this->getCType($container, $config), - 'tx_gridelements_backend_layout' => '' - ]; - - if (isset($config['map']) && is_array($config['map']) && !empty($container['pi_flexform'])) { - $data = $this->addMappedValues($data, $container, $config['map']); - } - - $this->updateElement($container['uid'], $data); - } - - protected function processContainerMigrationChild(array $child, array $container): void - { - $this->updateElement( - $child['uid'], - [ - 'hidden' => $child['hidden'] ?: $container['hidden'], - 'colPos' => $child['tx_gridelements_columns'] + $this->colPosOffset, - 'tx_container_parent' => $child['tx_gridelements_container'], - 'tx_gridelements_columns' => 0, - 'tx_gridelements_container' => 0, - ] - ); - } - - protected function getCType(array $container, array $config): string - { - if (is_array($config['CType'])) { - $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); - $result = $config['CType']['matches'][$value] ?? null; - } else { - $result = $config['CType'] ?? null; - } - - if (empty($result)) { - throw new \Exception('CType must always be set'); - } - - return $result; - } - - protected function addMappedValues(array $data, array $container, array $config): array - { - foreach ($config as $from => $to) { - try { - $value = ArrayUtility::getValueByPath($container, $from); - if (empty($container[$to]) && !empty($value)) { - $data[$to] = $value; - } - } catch (\throwable) {} - } - - return $data; - } - - - protected function updateElement(int $uid, array $changes): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); - } - - protected function getGridContainerChildren(int $containerUid): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - - return $queryBuilder - ->select('*') - ->where( - $queryBuilder->expr()->eq( - 'tx_gridelements_container', - $queryBuilder->createNamedParameter($containerUid, \PDO::PARAM_INT) - ) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getGridElementsByLayout(string $layout): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - $expr = $queryBuilder->expr(); - - return $queryBuilder - ->select('*') - ->where( - $expr->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')), - $expr->eq('tx_gridelements_backend_layout', $queryBuilder->createNamedParameter($layout)) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($table); - - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - - $queryBuilder->from($table); - - return $queryBuilder; - } -} diff --git a/Classes/Updates/ParentChildToContainerMigration.php b/Classes/Updates/ParentChildToContainerMigration.php deleted file mode 100644 index 685daab..0000000 --- a/Classes/Updates/ParentChildToContainerMigration.php +++ /dev/null @@ -1,106 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - public function getTitle(): string - { - return 'Migrate parent/child to container'; - } - - public function getDescription(): string - { - return 'Migrates content elements that are type parent/child to corresponding container elements'; - } - - public function getPrerequisites(): array - { - return [ - DatabaseUpdatedPrerequisite::class - ]; - } - - public function updateNecessary(): bool - { - return $this->hasRecordsToUpdate(); - } - - public function executeUpdate(): bool - { - $updateSuccessful = true; - try { - $this->service->migrate(); - } catch (\Exception $exception) { - $this->logger->log(LogLevel::ERROR, $exception->getMessage()); - $updateSuccessful = false; - } - - return $updateSuccessful; - } - - protected function hasRecordsToUpdate(): bool - { - $queryBuilder = $this->getPreparedQueryBuilder(); - $tableColumns = $queryBuilder - ->getConnection() - ->createSchemaManager() - ->listTableColumns(self::TABLE_NAME); - return isset($tableColumns['parent']) - && $queryBuilder - ->count('uid') - ->executeQuery() - ->fetchOne() > 0; - } - - protected function getPreparedQueryBuilder(): QueryBuilder - { - $queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME); - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - $queryBuilder - ->from(self::TABLE_NAME) - ->where( - // check if there are still records that have parent instead of tx_container_parent - $queryBuilder->expr()->gt('parent', 0) - ); - return $queryBuilder; - } - - protected function getConnectionPool(): ConnectionPool - { - return GeneralUtility::makeInstance(ConnectionPool::class); - } -} diff --git a/Classes/Updates/ParentChildToContainerService.php b/Classes/Updates/ParentChildToContainerService.php deleted file mode 100644 index 737ed75..0000000 --- a/Classes/Updates/ParentChildToContainerService.php +++ /dev/null @@ -1,168 +0,0 @@ - [ 'CType' => 'container-downloads' ], -]; -*/ - -class ParentChildToContainerService -{ - private const TABLE_NAME = 'tt_content'; - - protected int $colPosOffset = 0; - - protected array $configuration = []; - - public function __construct( - protected ConnectionPool $connectionPool, - protected DataHandler $dataHandler, - protected FlexFormService $flexFormService, - ) { - $config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']; - $this->colPosOffset = $config['childParentColPosOffset'] ?? 0; - $this->configuration = $config['childParentMigrationMapping'] ?? []; - } - - public function migrate(): void - { - $this->initializeDataHandler(); - $this->migrateConfiguredContainer(); - } - - protected function initializeDataHandler(): void - { - $backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class); - $backendUser->user = [ - 'uid' => 0, - 'admin' => 1, - ]; - $this->dataHandler->start([], [], $backendUser); - } - - - protected function migrateConfiguredContainer(): void - { - array_walk($this->configuration, function($config, $key) { - $parents = $this->getParentsByCType((string)$key); - foreach ($parents as $parent) { - $this->processParentMigration($parent, $config); - } - }); - } - - protected function processParentMigration(array $parent, array $config): void - { - $children = $this->getChildren($parent['uid']); - foreach ($children as $child) { - $this->processChildMigration($child, $parent); - } - - $this->updateElement( - $parent['uid'], - [ - 'CType' => $this->getCType($parent, $config), - 'children' => 0, - ] - ); - } - - protected function processChildMigration(array $child, array $parent): void - { - $this->updateElement( - $child['uid'], - [ - 'tx_container_parent' => $parent['uid'], - 'colPos' => $child['colPos'] + $this->colPosOffset, - 'parent' => 0, - ] - ); - } - - protected function getCType(array $container, array $config): string - { - if (is_array($config['CType'])) { - $value = ArrayUtility::getValueByPath($container, $config['CType']['search']); - $result = $config['CType']['matches'][$value] ?? null; - } else { - $result = $config['CType'] ?? null; - } - - if (empty($result)) { - throw new \Exception('CType must always be set'); - } - - return $result; - } - - - protected function updateElement(int $uid, array $changes): void - { - $this->connectionPool - ->getConnectionForTable(self::TABLE_NAME) - ->update(self::TABLE_NAME, $changes, ['uid' => $uid]); - } - - protected function getChildren(int $parentUid): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - - return $queryBuilder - ->select('*') - ->where( - $queryBuilder->expr()->eq( - 'parent', - $queryBuilder->createNamedParameter($parentUid, \PDO::PARAM_INT) - ) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getParentsByCType(string $cType): array - { - $queryBuilder = $this->getQueryBuilderForTable(); - $expr = $queryBuilder->expr(); - - return $queryBuilder - ->select('*') - ->where( - $expr->eq('CType', $queryBuilder->createNamedParameter($cType)) - ) - ->orderBy('sorting') - ->executeQuery() - ->fetchAllAssociative(); - } - - protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder - { - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) - ->getQueryBuilderForTable($table); - - $queryBuilder->getRestrictions() - ->removeByType(HiddenRestriction::class) - ->removeByType(StartTimeRestriction::class) - ->removeByType(EndTimeRestriction::class); - - $queryBuilder->from($table); - - return $queryBuilder; - } -} diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php index 9659830..8232df3 100644 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -2,6 +2,17 @@ declare(strict_types=1); +/* + * This file is developed by evoWeb. + * + * 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. + */ + namespace Evoweb\EwBase\User; use TYPO3\CMS\Core\Utility\GeneralUtility; diff --git a/Classes/ViewHelpers/Array/AddViewHelper.php b/Classes/ViewHelpers/Array/AddViewHelper.php index 1d0b5c9..06357e8 100644 --- a/Classes/ViewHelpers/Array/AddViewHelper.php +++ b/Classes/ViewHelpers/Array/AddViewHelper.php @@ -1,6 +1,6 @@ registerArgument('array', 'array', 'Array to add value to'); $this->registerArgument('key', 'string', 'Key to add value by', true); $this->registerArgument('value', 'mixed', 'Value to add'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $array = $arguments['array'] ?: []; - $key = $arguments['key']; - $value = !is_null($arguments['value']) ? $arguments['value'] : $renderChildrenClosure(); + public function render(): array + { + $array = $this->arguments['array'] ?: []; + $key = $this->arguments['key']; + $value = !is_null($this->arguments['value']) ? $this->arguments['value'] : $this->renderChildren(); return array_merge($array, [$key => $value]); } diff --git a/Classes/ViewHelpers/Array/ExplodeViewHelper.php b/Classes/ViewHelpers/Array/ExplodeViewHelper.php index 089b68e..0d04347 100755 --- a/Classes/ViewHelpers/Array/ExplodeViewHelper.php +++ b/Classes/ViewHelpers/Array/ExplodeViewHelper.php @@ -1,5 +1,7 @@ arguments['content'] ?? $this->renderChildren(); + $glue = $this->resolveGlue($this->arguments); + $content = call_user_func_array($this->method, [$glue, $content]); - $as = $arguments['as']; + $as = $this->arguments['as']; if ($as !== null) { - $templateVariableContainer = $renderingContext->getVariableProvider(); + $templateVariableContainer = $this->renderingContext->getVariableProvider(); $templateVariableContainer->add($as, $content); - $output = $renderChildrenClosure(); + $output = $this->renderChildren(); $templateVariableContainer->remove($as); } else { $output = $content; @@ -73,7 +68,7 @@ class ExplodeViewHelper extends AbstractViewHelper return $output; } - protected static function resolveGlue(array $arguments): string + protected function resolveGlue(array $arguments): string { $glue = $arguments['glue']; if (str_contains($glue, ':') && strlen($glue) > 1) { diff --git a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php index 8449574..0fa73a2 100644 --- a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php +++ b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php @@ -15,24 +15,21 @@ declare(strict_types=1); namespace Evoweb\EwBase\ViewHelpers\Be; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Utility\BackendUtility; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Imaging\IconFactory; +use TYPO3\CMS\Core\Imaging\IconSize; use TYPO3\CMS\Core\Localization\LanguageService; +use TYPO3\CMS\Core\Resource\ProcessedFile; use TYPO3\CMS\Core\Type\Bitmask\Permission; use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; class ThumbnailViewHelper extends AbstractViewHelper { - use CompileWithRenderStatic; - - /** - * Initializes the arguments - */ - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('row', 'array', 'content data', true); @@ -43,34 +40,47 @@ class ThumbnailViewHelper extends AbstractViewHelper /** * Render a constant * - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * * @return string Value of constant */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $row = $arguments['row']; - $tableName = $arguments['tableName']; - $fieldName = $arguments['fieldName']; + public function render(): string + { + $table = $this->arguments['tableName']; + $field = $this->arguments['fieldName']; + $row = $this->arguments['row']; - return self::linkEditContent( - BackendUtility::thumbCode(row: $row, table: $tableName, field: $fieldName, linkInfoPopup: false), - $row + $fileReferences = BackendUtility::resolveFileReferences($table, $field, $row); + $fileObject = is_array($fileReferences) ? $fileReferences[0]->getOriginalFile() : null; + + if ($fileObject && $fileObject->isMissing()) { + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $label = $this->getLanguageService()->sL( + 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing' + ); + return $iconFactory + ->getIcon('mimetypes-other-other', IconSize::MEDIUM, 'overlay-missing') + ->setTitle($label . ' ' . $fileObject->getName()) + ->render(); + } + + $previewFile = $fileObject->process( + ProcessedFile::CONTEXT_IMAGEPREVIEW, + [ + 'width' => 64, + 'height' => 64, + ] ); + + return $this->linkEditContent('', $row ); } - protected static function linkEditContent(string $linkText, $row): string + protected function linkEditContent(string $linkText, $row): string { if (empty($linkText)) { return $linkText; } - $backendUser = self::getBackendUser(); + $backendUser = $this->getBackendUser(); if ( $backendUser->check('tables_modify', 'tt_content') && $backendUser->recordEditAccessInternals('tt_content', $row) @@ -86,14 +96,15 @@ class ThumbnailViewHelper extends AbstractViewHelper $row['uid'] => 'edit', ], ], - 'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri() + // @extensionScannerIgnoreLine + 'returnUrl' => $this->getRequest()->getAttribute('normalizedParams')->getRequestUri() . '#element-tt_content-' . $row['uid'], ]; /** @var UriBuilder $uriBuilder */ $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); $url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters); return '' . $linkText . ''; @@ -101,13 +112,18 @@ class ThumbnailViewHelper extends AbstractViewHelper return $linkText; } - protected static function getBackendUser(): BackendUserAuthentication + protected function getBackendUser(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } - protected static function getLanguageService(): LanguageService + protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; } + + protected function getRequest(): ServerRequestInterface + { + return $GLOBALS['TYPO3_REQUEST']; + } } diff --git a/Classes/ViewHelpers/Condition/InArrayViewHelper.php b/Classes/ViewHelpers/Condition/InArrayViewHelper.php index bc713f5..36b47a2 100644 --- a/Classes/ViewHelpers/Condition/InArrayViewHelper.php +++ b/Classes/ViewHelpers/Condition/InArrayViewHelper.php @@ -1,14 +1,21 @@ registerArgument('haystack', 'array', 'haystack', true); $this->registerArgument('needle', 'mixed', 'needle', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + public static function verdict(array $arguments, RenderingContextInterface $renderingContext): bool { $array = $arguments['haystack']->toArray(); return in_array($arguments['needle'], $array); diff --git a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php index efb6b6f..7b28cc6 100644 --- a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php +++ b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php @@ -1,14 +1,21 @@ registerArgument('haystack', 'string', 'haystack', true); $this->registerArgument('needle', 'string', 'need', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + public static function verdict(array $arguments, RenderingContextInterface $renderingContext): bool { - return false !== strpos($arguments['haystack'], $arguments['needle']); + return str_contains($arguments['haystack'], $arguments['needle']); } } diff --git a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php index 0875df1..fe4a5b2 100644 --- a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php +++ b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php @@ -1,24 +1,27 @@ isDevelopment(); } diff --git a/Classes/ViewHelpers/Context/ProductionViewHelper.php b/Classes/ViewHelpers/Context/ProductionViewHelper.php index f5356bb..e57fc31 100644 --- a/Classes/ViewHelpers/Context/ProductionViewHelper.php +++ b/Classes/ViewHelpers/Context/ProductionViewHelper.php @@ -1,24 +1,27 @@ isProduction(); } diff --git a/Classes/ViewHelpers/Context/StagingViewHelper.php b/Classes/ViewHelpers/Context/StagingViewHelper.php index a76003e..bbbae08 100644 --- a/Classes/ViewHelpers/Context/StagingViewHelper.php +++ b/Classes/ViewHelpers/Context/StagingViewHelper.php @@ -1,24 +1,27 @@ registerArgument('data', 'array', 'The data array of content element', true); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $templateVariableContainer = $renderingContext->getVariableProvider(); + public function render(): string + { + $templateVariableContainer = $this->renderingContext->getVariableProvider(); /** @var FlexFormTools $flexFormTools */ $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); - $data = $arguments['data']; + $data = $this->arguments['data']; if (is_array($data['pi_flexform'])) { $data['pi_flexform'] = $flexFormTools->flexArray2Xml($data['pi_flexform']); } $templateVariableContainer->add('data', $data); - $output = $renderChildrenClosure(); + $output = $this->renderChildren(); $templateVariableContainer->remove('data'); return $output; diff --git a/Classes/ViewHelpers/FlexFormViewHelper.php b/Classes/ViewHelpers/FlexFormViewHelper.php index cd7fc27..ea793b0 100644 --- a/Classes/ViewHelpers/FlexFormViewHelper.php +++ b/Classes/ViewHelpers/FlexFormViewHelper.php @@ -1,6 +1,6 @@ registerArgument('data', 'array', 'Array to get flex form data from', true); @@ -47,33 +45,23 @@ class FlexFormViewHelper extends AbstractViewHelper $this->registerArgument('as', 'string', 'Name of the variable to assign', false, 'flexFormData'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { + public function render(): string + { $data = []; - if (is_array($arguments['data'])) { - if (isset($arguments['data'][$arguments['fieldName']])) { - $data = is_array($arguments['data'][$arguments['fieldName']]) ? - $arguments['data'][$arguments['fieldName']] : - GeneralUtility::xml2array($arguments['data'][$arguments['fieldName']]); + if (is_array($this->arguments['data'])) { + if (isset($this->arguments['data'][$this->arguments['fieldName']])) { + $data = is_array($this->arguments['data'][$this->arguments['fieldName']]) ? + $this->arguments['data'][$this->arguments['fieldName']] : + GeneralUtility::xml2array($this->arguments['data'][$this->arguments['fieldName']]); $data = $data['data'] ?? $data; } } - $templateVariableContainer = $renderingContext->getVariableProvider(); - $templateVariableContainer->add($arguments['as'], $data); - $content = $renderChildrenClosure(); - $templateVariableContainer->remove($arguments['as']); + $templateVariableContainer = $this->renderingContext->getVariableProvider(); + $templateVariableContainer->add($this->arguments['as'], $data); + $content = $this->renderChildren(); + $templateVariableContainer->remove($this->arguments['as']); return $content; } diff --git a/Classes/ViewHelpers/HashViewHelper.php b/Classes/ViewHelpers/HashViewHelper.php deleted file mode 100644 index 0b61138..0000000 --- a/Classes/ViewHelpers/HashViewHelper.php +++ /dev/null @@ -1,60 +0,0 @@ -registerArgument('action', 'string', 'Target action'); - $this->registerArgument('arguments', 'array', 'Arguments for the controller action, associative array'); - } - - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return string - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $result = ''; - if ( - $arguments['action'] !== null - && $arguments['arguments'] !== null - && isset($arguments['arguments']['user']) - ) { - $result = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac( - $arguments['action'] . '::' . $arguments['arguments']['user'] - ); - } - - return $result; - } -} diff --git a/Classes/ViewHelpers/Iterator/AddViewHelper.php b/Classes/ViewHelpers/Iterator/AddViewHelper.php deleted file mode 100644 index bb24422..0000000 --- a/Classes/ViewHelpers/Iterator/AddViewHelper.php +++ /dev/null @@ -1,56 +0,0 @@ -registerArgument('array', 'array', 'Array to add value to'); - $this->registerArgument('key', 'string', 'Key to add value by', true); - $this->registerArgument('value', 'mixed', 'Value to add'); - } - - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $array = $arguments['array'] ?: []; - $key = $arguments['key']; - $value = $arguments['value'] ?: $renderChildrenClosure(); - - return array_merge($array, [$key => $value]); - } -} diff --git a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php b/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php deleted file mode 100644 index e50139f..0000000 --- a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php +++ /dev/null @@ -1,93 +0,0 @@ - ewb:iterator.explode(glue: 'constant:LF')} - * - * - * {as} - * - */ -class ExplodeViewHelper extends AbstractViewHelper -{ - use CompileWithRenderStatic; - - protected static string $method = 'explode'; - - public function initializeArguments() - { - $this->registerArgument( - 'as', - 'string', - 'Template variable name to assign; if not specified the ViewHelper returns the variable instead.' - ); - $this->registerArgument('content', 'string', 'String to be exploded by glue'); - $this->registerArgument( - 'glue', - 'string', - 'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" ' . - '(fx "constant:LF" for linefeed as glue)', - false, - ',' - ); - } - - /** - * Render method - * - * @return string|array - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $content = $arguments['content'] ?? $renderChildrenClosure(); - $glue = static::resolveGlue($arguments); - $content = call_user_func_array(static::$method, [$glue, $content]); - - $as = $arguments['as']; - if (true === empty($as)) { - $output = $content; - } else { - $templateVariableContainer = $renderingContext->getVariableProvider(); - $templateVariableContainer->add($as, $content); - $output = $renderChildrenClosure(); - $templateVariableContainer->remove($as); - } - return $output; - } - - protected static function resolveGlue(array $arguments): string - { - $glue = $arguments['glue']; - if (false !== strpos($glue, ':') && 1 < strlen($glue)) { - // glue contains a special type identifier, resolve the actual glue - list ($type, $value) = explode(':', $glue); - switch ($type) { - case 'constant': - $glue = constant($value); - break; - default: - $glue = $value; - } - } - return $glue; - } -} diff --git a/Classes/ViewHelpers/PublicPathViewHelper.php b/Classes/ViewHelpers/PublicPathViewHelper.php index 769f001..f9c1329 100644 --- a/Classes/ViewHelpers/PublicPathViewHelper.php +++ b/Classes/ViewHelpers/PublicPathViewHelper.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Evoweb\EwBase\ViewHelpers; - /* * This file is developed by evoWeb. * @@ -15,10 +13,11 @@ namespace Evoweb\EwBase\ViewHelpers; * LICENSE.txt file that was distributed with this source code. */ +namespace Evoweb\EwBase\ViewHelpers; + +use TYPO3\CMS\Core\Resource\Exception\InvalidFileException; use TYPO3\CMS\Core\Utility\PathUtility; -use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; -use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; /** * = Examples = @@ -34,34 +33,23 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; */ class PublicPathViewHelper extends AbstractViewHelper { - use CompileWithRenderStatic; - /** * @var boolean */ protected $escapeOutput = false; - protected static ?array $frontendGroupIds = null; - - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('path', 'string', 'Extension resource path', true); } /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return bool + * @throws InvalidFileException */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $path = (string)$arguments['path']; + public function renderStatic(): string + { + $path = (string)$this->arguments['path']; return PathUtility::getPublicResourceWebPath($path); } } diff --git a/Classes/ViewHelpers/ReplaceViewHelper.php b/Classes/ViewHelpers/ReplaceViewHelper.php index 77a9837..a9fe781 100644 --- a/Classes/ViewHelpers/ReplaceViewHelper.php +++ b/Classes/ViewHelpers/ReplaceViewHelper.php @@ -1,6 +1,6 @@ registerArgument('value', 'string', 'String to replace in'); $this->registerArgument('search', 'string', 'Search string'); $this->registerArgument('replace', 'string', 'Replace value'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * @return null - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $content = $arguments['value']; + public function render(): string + { + $content = $this->arguments['value']; if ($content === null) { - $content = $renderChildrenClosure(); + $content = $this->renderChildren(); } - return str_replace($arguments['search'], $arguments['replace'], $content); + return str_replace($this->arguments['search'], $this->arguments['replace'], $content); } } diff --git a/Classes/ViewHelpers/SvgViewHelper.php b/Classes/ViewHelpers/SvgViewHelper.php index 2290936..6dc418f 100644 --- a/Classes/ViewHelpers/SvgViewHelper.php +++ b/Classes/ViewHelpers/SvgViewHelper.php @@ -1,6 +1,6 @@ * */ -class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class SvgViewHelper extends AbstractViewHelper { - use CompileWithRenderStatic; - /** * ViewHelper returns HTML, thus we need to disable output escaping * @@ -75,30 +74,25 @@ class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper public function initializeArguments(): void { $this->registerArgument('identifier', 'string', 'Identifier of the icon as registered in the Icon Registry.', true); - $this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, Icon::SIZE_SMALL); - $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false); + $this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, IconSize::SMALL); + $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.'); $this->registerArgument('state', 'string', 'Sets the state of the icon. All values of the Icons.states enum are allowed, these are: "default" and "disabled".', false, IconState::STATE_DEFAULT); - $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false); + $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.'); } /** * Prints icon html for $identifier key * - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext * @return string */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $identifier = $arguments['identifier']; - $size = $arguments['size']; - $overlay = $arguments['overlay']; - $state = IconState::cast($arguments['state']); - $alternativeMarkupIdentifier = $arguments['alternativeMarkupIdentifier']; + public function render(): string + { + $identifier = $this->arguments['identifier']; + $size = $this->arguments['size']; + $overlay = $this->arguments['overlay']; + $state = IconState::tryFrom($this->arguments['state']); + $alternativeMarkupIdentifier = $this->arguments['alternativeMarkupIdentifier']; + /** @var IconFactory $iconFactory */ $iconFactory = GeneralUtility::makeInstance(IconFactory::class); return $iconFactory->getIcon($identifier, $size, $overlay, $state)->getMarkup($alternativeMarkupIdentifier); } diff --git a/Classes/ViewHelpers/TrimViewHelper.php b/Classes/ViewHelpers/TrimViewHelper.php index 68683ff..5d1db02 100644 --- a/Classes/ViewHelpers/TrimViewHelper.php +++ b/Classes/ViewHelpers/TrimViewHelper.php @@ -1,6 +1,6 @@ registerArgument('content', 'string', 'Content to be trimmed'); - $this->registerArgument('characters', 'string', 'Characters to be removed', false); + $this->registerArgument('characters', 'string', 'Characters to be removed'); } - /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return string - */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $content = $arguments['content'] ? $arguments['content'] : $renderChildrenClosure(); - $characters = $arguments['characters'] ? $arguments['characters'] : null; + public function render(): string + { + $content = $this->arguments['content'] ?: $this->renderChildren(); + $characters = $this->arguments['characters'] ?: null; if ($characters !== null) { $content = trim($content, $characters); diff --git a/Configuration/Icons.php b/Configuration/Icons.php index 4220dea..8962136 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -1,6 +1,5 @@ SvgIconProvider::class, 'source' => 'EXT:ew_base/Resources/Public/Icons/Extension_16.svg', ], - 'ew-usercentrics' => [ - 'provider' => BitmapIconProvider::class, - 'source' => 'EXT:ew_base/Resources/Public/Icons/usercentrics.svg', - ], ]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 8050c5b..3f7994e 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -16,29 +16,14 @@ services: Evoweb\EwBase\EventListener\CssMerger: tags: ['event.listener'] + public: true Evoweb\EwBase\EventListener\JsMerger: tags: ['event.listener'] + public: true - Evoweb\EwBase\EventListener\IsContentUsedOnPageLayout: + Evoweb\EwBase\EventListener\ReleaseToolbarItem: tags: ['event.listener'] - - Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem: - tags: ['backend.toolbar.item'] - - Evoweb\EwBase\Hooks\UsercentricsPostRenderHook: - public: true - - Evoweb\EwBase\Updates\GridelementsToContainerMigration: - public: true - - Evoweb\EwBase\Updates\GridelementsToContainerService: - public: true - - Evoweb\EwBase\Updates\ParentChildToContainerMigration: - public: true - - Evoweb\EwBase\Updates\ParentChildToContainerService: public: true Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow: diff --git a/Configuration/Sets/EwBase/settings.definitions.yaml b/Configuration/Sets/EwBase/settings.definitions.yaml index 31732ee..38aef6d 100644 --- a/Configuration/Sets/EwBase/settings.definitions.yaml +++ b/Configuration/Sets/EwBase/settings.definitions.yaml @@ -1,33 +1,11 @@ +categories: + ew-base: + label: 'Evoweb Base' + settings: - ew-base.userCentrics.loader.preload: - default: '' - label: 'UserCentrics loader preloader' - type: string - description: 'Preloader for loader' - ew-base.userCentrics.loader.template: - default: '' - label: 'UserCentrics loader template' - type: string - description: 'Template for loader' - - ew-base.userCentrics.main.preload: - default: '' - label: 'UserCentrics main preloader' - type: string - description: 'Preloader for main' - ew-base.userCentrics.main.template: - default: '' - label: 'UserCentrics main template' - type: string - description: 'Template for main' - - ew-base.userCentrics.block.preload: - default: '' - label: 'UserCentrics block preloader' - type: string - description: 'Preloader for block' - ew-base.userCentrics.block.template: - default: '' - label: 'UserCentrics block template' - type: string - description: 'Template for block' + ew-base.inlineCssStyles: + default: false + label: 'Inline css' + type: bool + description: 'Weather the concatenated css should get added inline style tag or a css file' + category: ew-base diff --git a/Configuration/Sets/EwBase/settings.yaml b/Configuration/Sets/EwBase/settings.yaml index a40b5a9..b61c029 100644 --- a/Configuration/Sets/EwBase/settings.yaml +++ b/Configuration/Sets/EwBase/settings.yaml @@ -1,22 +1,2 @@ ew-base: - userCentrics: - loader: - preload: | - - - - template: | - - main: - preload: | - - - - - template: | - - block: - preload: | - - template: | - + inlineCssStyles: false diff --git a/Configuration/TCA/Overrides/pages.php b/Configuration/TCA/Overrides/pages.php index 6a697aa..02ebb34 100644 --- a/Configuration/TCA/Overrides/pages.php +++ b/Configuration/TCA/Overrides/pages.php @@ -10,15 +10,15 @@ $newColumns = [ 'type' => 'group', 'allowed' => 'tt_content', 'size' => 1, + 'relationship' => 'manyToOne', + 'default' => 0, 'maxitems' => 1, - 'minitems' => 0, 'suggestOptions' => [ 'default' => [ 'additionalSearchFields' => 'header, bodytext', 'searchWholePhrase' => false ] ], - 'default' => 0, 'behaviour' => [ 'allowLanguageSynchronization' => true ] diff --git a/README.md b/README.md index 99d4154..adc44fa 100644 --- a/README.md +++ b/README.md @@ -1,19 +1 @@ # ew-base -## Usercentrics Hook Guide - -Add this snippet to your config/sites to enable the hook -```yaml -usercentrics: - - - id: LNxzTaK8j - version: loader - useBlocker: false - applicationContext: "Production/Staging\r\nProduction\r\nDevelopment\r\n" -``` - -``` -id - Usercentrics Id -version (loader | main) - loader is the v2 and main is v1 in usercentrics context -useBlocker - if the content blocker script should be rendered -applicationContext - multiline string of application contexts that the snippets should be rendered in -``` diff --git a/Resources/Private/Language/locallang_core.xlf b/Resources/Private/Language/locallang_core.xlf index ef33b70..f226a6f 100644 --- a/Resources/Private/Language/locallang_core.xlf +++ b/Resources/Private/Language/locallang_core.xlf @@ -3,9 +3,6 @@
- - Release information - Release tag diff --git a/Resources/Private/Language/locallang_siteconfiguration.xlf b/Resources/Private/Language/locallang_siteconfiguration.xlf deleted file mode 100644 index 682ac47..0000000 --- a/Resources/Private/Language/locallang_siteconfiguration.xlf +++ /dev/null @@ -1,33 +0,0 @@ - - - -
- - - UserCentrics - - - Max one configuration per site - - - - Usercentrics configuration - - - ID - - - ID defining what user centrics account to use - - - Version - - - Application Context - - - use Blocker - - - - diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html deleted file mode 100644 index aa01a16..0000000 --- a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html +++ /dev/null @@ -1,14 +0,0 @@ - - - -
- - - - - - - - diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html deleted file mode 100644 index 93c156f..0000000 --- a/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/Resources/Public/Icons/usercentrics.svg b/Resources/Public/Icons/usercentrics.svg deleted file mode 100644 index 6d44c30..0000000 --- a/Resources/Public/Icons/usercentrics.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/composer.json b/composer.json index d9f7594..e1292ee 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,6 @@ { "name": "evoweb/ew-base", "type": "typo3-cms-extension", - "version": "1.0.0", "autoload": { "psr-4": { "Evoweb\\EwBase\\": "Classes/" diff --git a/ext_conf_template.txt b/ext_conf_template.txt deleted file mode 100644 index b51c2fa..0000000 --- a/ext_conf_template.txt +++ /dev/null @@ -1,2 +0,0 @@ -# cat=basic//10; type=boolean; label= Weather the content of css files gets inserted to the source code of the page as inline css -inlineCssStyles = 0 diff --git a/ext_localconf.php b/ext_localconf.php index 9f47c15..9741611 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -9,7 +9,7 @@ use Evoweb\EwBase\Form\Element\PickColorFromImage; $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [ 'nodeName' => 'pick-color-from-image', - 'priority' => '70', + 'priority' => 70, 'class' => PickColorFromImage::class, ]; })(); diff --git a/ext_tables.sql b/ext_tables.sql deleted file mode 100644 index 232ee2c..0000000 --- a/ext_tables.sql +++ /dev/null @@ -1,7 +0,0 @@ -# -# Table structure for table 'pages' -# -CREATE TABLE pages -( - sectionIndex_uid int(11) unsigned DEFAULT '0' NOT NULL -);